unit UAtomBase;

{
     AtomBase
     base classes for Atom feeds and etnries

      Copyright (c) 1999-2004 marumoto. witha system co.ltd, Tokyo Japan
      http://www.witha.jp/eXeries/

   1. This software is provided as it is, without any kind of
      warranty given. Use it at Your own risk.The author is not
      responsible for any consequences of use of this software.
   2. The origin of this software may not be mispresented, You
      must not claim that You wrote the original software. If
      You use this software in any kind of product, it would be
      appreciated that there in a information box, or in the
      documentation would be an acknowledgement.
   3. You may not have any income from distributing this source
      (or altered version of it) to other developers. When You
      use this product in a comercial package, the source may
      not be charged seperatly.
   4. Altered versions must be plainly marked as such, and must
      not be misrepresented as being the original software.



  History:
    initial release.

  TODO:

    Too much dupe stuff. contibuters, authors, links from xml node. they are common feed/entry/(category doc).

    Traverse parent atom:author and atom:rights from Feed if avalable

    type="xhtml" content of the element needs to be treated as xml not text. Only <atom:content> and summary is fixed.

  Known Issues:
    xml:base and xml:lang support is limited. (Only title, summary, content, etc)
    foreign markup in "category","author","contributer" and "link" will not preserved.

  MEMO:
    foreign markup is preserved mostly.

    If TAtomColection.AcceptMediaRangeList is empty, then that means nothing allowed to post.

  PLAN:
    easy access for extensions and foreign markups
}


{$DEFINE AtomStrictMode}
{ $ U N D E F AtomStrictMode }

interface

uses
  Windows,SysUtils, Classes,      {basic classes}
  MSXML2_TLB,ActiveX,ComObj,      {xml document manupilation}
  WinInet,                        {for InternetCombineURL()}
  XSBuiltIns,DateUtils           {xml datetime parse and conversion, plus YearOf function}
  //UAtomUtils                      {utility unit for some methods}
  ;

//type
//  TGetWStrProc = procedure(const S: WideString) of object;


type
  TErrorInfo = class(TObject)
  public
    Title:WideString;
    Description:WideString;
  end;
  TErrorEvent = procedure(Sender: TObject; Error: TErrorInfo) of object;

Type
  TXMLErrorInfo = class(TErrorInfo)
  public
    Uri:string;
    ErrorCode:Integer;
    Reason:WideString;
    SrcText:WideString;
    Line:integer;
    LinePos:integer;
  end;
  TXMLErrorEvent = procedure(Sender: TObject; Error: TXMLErrorInfo) of object;


type
  EXMLParseError = class(Exception)
  private
    FXMLError:TXMLErrorInfo;
  public
    constructor Create(XMLError:TXMLErrorInfo);
    destructor Destroy; override;
    property XMLError: TXMLErrorInfo read FXMLError write FXMLError;
  end;


type
  EAtomError = class(Exception)
  private
    FError:TErrorInfo;
  public
    constructor Create(Error:TErrorInfo);
    destructor Destroy; override;
    property Error: TErrorInfo read FError write FError;
  end;

type
  TTextType =(ttText,ttHtml,ttXhtml,ttOther);

type
  TAtomCommonAttributes = class(TObject)
  private
    FBase:string;
    FParentBase:string;
    function CalculateUri(baseUri:string; partsUri:string):string;
    function getBase:string;
  public
    Lang:string;
  published
    property parentBase:string read FParentBase write FParentBase;
    property Base : string read getBase write FBase;
  end;

type
  TAtomUri = class(TAtomCommonAttributes)
  private
    FUri:string;
    function getAbsoluteUri:string; overload;
  published
    constructor Create(BaseUri:string);
    property  Uri : string read getAbsoluteUri write FUri;
  end;

type
  TAtomTextConstruct = class(TAtomCommonAttributes)
  public
    TextType:TTextType;
    Text:WideString;
    constructor Create;
  end;

type
  TAtomPerson = class(TAtomCommonAttributes)
  private
    FXML:WideString;
  public
    Name:WideString;
    Uri:TAtomUri;
    Email:WideString;
    function asXMLObj(documentElementName:string):IXMLDOMDocument2;
    constructor Create;
    //TODO:constructor Create(fromXML:WideString; baseUri:string);overload;
    destructor Destroy; override;
  end;

  TAtomPersonList = class(TList)
  private
    function Get(Index: Integer): TAtomPerson;
    procedure Put(Index: Integer; const Value: TAtomPerson);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomPerson read Get write Put; default;
  end;

type
  TAtomCategory = class(TAtomCommonAttributes)
  private
    FXML:WideString;
    function getXMLObj:IXMLDOMDocument2;
  public
    Term:WideString;
    CategoryLabel:WideString;
    Scheme:TAtomUri;
    property  asXMLObj : IXMLDOMDocument2 read getXMLObj;
    constructor Create;overload;
    constructor Create(fromXML:WideString; baseUri:string);overload;
    destructor Destroy; override;
  end;

  TAtomCategoryList = class(TList)
  private
    function Get(Index: Integer): TAtomCategory;
    procedure Put(Index: Integer; const Value: TAtomCategory);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomCategory read Get write Put; default;
  end;

Type
  TAtomCategories = class(TAtomCommonAttributes)
  private
    FXML:WideString;
  public
    Fixed:boolean;
    Scheme:TAtomUri;
    Href:TAtomUri;
    CategoryList:TAtomCategoryList;
    constructor Create;
    procedure setCategories(fromXML:WideString;baseUri:string);
    destructor Destroy; override;
    function asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
  end;

type
  TAtomContent = class(TAtomCommonAttributes)
  public
    ContentType:string;
    Text:WideString;
    Src:TAtomUri;
    constructor Create;
    //TODO:constructor Create(fromXML:WideString; baseUri:string);overload;
    destructor Destroy; override;
  end;

type
  TAtomLink = class(TAtomCommonAttributes)
  private
    FXML:WideString;
    function getXMLObj:IXMLDOMDocument2;
  public
    Rel:string; //[edit,edit-media],enclosure,via,self,related,alternate,etc
    ContentType:string;
    Href:TAtomUri;
    Length:WideString;
    Title:WideString;
    Hreflang:string;
    property  asXMLObj : IXMLDOMDocument2 read getXMLObj;
    constructor Create;
    destructor Destroy; override;
  end;

  TAtomLinkList = class(TList)
  private
    function Get(Index: Integer): TAtomLink;
    procedure Put(Index: Integer; const Value: TAtomLink);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomLink read Get write Put; default;
  end;

  {TODO: create new TAtomEntryExtPub class inherited from TAtomEntry?}
type
  TAtomPubControl = class(TObject)
  public
    PubDraft:boolean;
  end;

type
 TAtomEntry = class(TAtomCommonAttributes)
  private
    function getEditUri:string;
    function getEditMediaUri:string;
    procedure setEditUri(strUri:string);
    //TODO: this should be in different place, eg ..
    function getObjNodeFromXML(XML:WideString):IXMLDOMNode;
  protected
    FXML:WideString;
    function getXMLObj:IXMLDOMDocument2;
  public
    Id:string;
    Title:TAtomTextConstruct;
    Links: TAtomLinkList;
    Authors:TAtomPersonList;
    Contributors:TAtomPersonList;
    Content:TAtomContent;
    Summary:TAtomTextConstruct;
    Categories:TAtomCategoryList;
    DateTimeUpdated:TDateTime;
    DateTimePublished:TDateTime;
    DateTimeEdited:TDateTime;
    Rights:TAtomTextConstruct;
    Source:WideString;
    PubControl:TAtomPubControl;
    constructor Create(baseUri:string = '');overload;
    constructor Create(fromXML:WideString; baseUri:string);overload;
    destructor Destroy; override;
    property  EditUri : string read getEditUri write setEditUri;
    property  EditMediaUri : string read getEditMediaUri;
    property  asXMLObj : IXMLDOMDocument2 read getXMLObj;

 end;

type
  TAtomEntryExt = class(TAtomEntry)
  public
    Etag:string;
    EtagMedia:string;
    FullRepresentation : boolean;
  end;

  TAtomEntryList = class(TList)
  private
    function Get(Index: Integer): TAtomEntry;
    procedure Put(Index: Integer; const Value: TAtomEntry);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomEntry read Get write Put; default;
  end;

type
  TAtomBinary = class(TAtomCommonAttributes)
  public
    Title:widestring;
    //TODO: need this to get media-type maybe?
    //FFilename:string;
    Binary:TMemoryStream;
    ContentType:string;
    //FLocation:string;
    constructor Create;
    destructor Destroy; override;
 end;

  TAtomBinaryExt = class(TAtomBinary)
  public
    Etag:string;
  end;

type
  TAtomFeed = class(TAtomCommonAttributes)
  private
    FXML:WideString;
    function getFirstUri:string;
    function getPrevUri:string;
    function getNextUri:string;
    function getLastUri:string;
    function getSelfUri:string;
  public
    Id:string;
    Title:TAtomTextConstruct;
    SubTitle:TAtomTextConstruct;
    Links: TAtomLinkList;
    Generator:string;
    Icon:TAtomUri;
    Logo:TAtomUri;
    Entries:TAtomEntryList;
    Authors:TAtomPersonList;
    Contributors:TAtomPersonList;
    Categories:TAtomCategoryList;
    DateTimeUpdated:TDateTime;
    Rights:TAtomTextConstruct;
    function asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
    function mergeEntriesFromFeed(feed:TAtomFeed):TAtomFeed;
    constructor Create(baseUri:string = '');overload;
    constructor Create(fromXML:WideString; baseUri:string);overload;
    destructor Destroy; override;
    property  prevUri : string read getPrevUri;
    property  nextUri : string read getNextUri;
    property  firstUri : string read getFirstUri;
    property  lastUri : string read getLastUri;
    property  selfUri : string read getSelfUri;
  end;

type
  TAtomCollection = class(TAtomCommonAttributes)
  private
    FXML:WideString;
  public
    Title:string;
    Href:TAtomUri;
    AcceptMediaRangeList:TStringList; //AcceptMediaRangeList is empty that means nothing allowed to post.
    Categories:TAtomCategories;  //"Create" auto.
    //property  asXMLObj : IXMLDOMDocument2 read getXMLObj;
    function asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
    constructor Create;overload;
    constructor Create(fromXML:WideString; baseUri:string; recurse:boolean=true);overload;
    destructor Destroy; override;
  end;

  TAtomCollectionList = class(TList)
  private
    function Get(Index: Integer): TAtomCollection;
    procedure Put(Index: Integer; const Value: TAtomCollection);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomCollection read Get write Put; default;
  end;

type
  TAtomWorkspace = class(TAtomCommonAttributes)
  private
    FXML:WideString;
  public
    Title:string;
    Collections:TAtomCollectionList;
    function asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
    constructor Create;overload;
    constructor Create(fromXML:WideString; baseUri:string; recurse:boolean=true);overload;
    destructor Destroy; override;
  end;

  TAtomWorkspaceList = class(TList)
  private
    function Get(Index: Integer): TAtomWorkspace;
    procedure Put(Index: Integer; const Value: TAtomWorkspace);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TAtomWorkspace read Get write Put; default;
  end;

type
  TAtomService = class(TAtomCommonAttributes)
  private
    FXML:WideString;
  public
    Workspaces:TAtomWorkspaceList;
    function asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
    constructor Create();overload;
    constructor Create(fromXML:WideString; baseUri:string; recurse:boolean=true);overload;
    destructor Destroy; override;
  end;


const
  Atom_NS='http://www.w3.org/2005/Atom';
  //Atom_PubControl_NS='http://www.w3.org/2005/Atom/PubControl'; //or whatever
  Atom_PubControl_NS='http://www.w3.org/2007/app';
  //Atom_PubControl_NS_Prefix='pub';
  Atom_PubControl_NS_Prefix='app';
  //Atom_APP_NS='http://purl.org/atom/app#';
  Atom_APP_NS='http://www.w3.org/2007/app';
  Atom_APP_NS_Prefix='app';
  XML_NS='http://www.w3.org/XML/1998/namespace';
  XML_NS_Prefix='xml';
  Witha_NS='http://www.witha.jp/Atom';
  Witha_NS_Prefix='witha';

resourcestring
  MsgStrResponseError = 'Response error';
  MsgStrResponseBodyIsEmpty = 'Response body is empty';
  MsgStrResponseContentTypeInvalid = 'Invalid content-type';
  MsgStrConnectionError = 'Connection error';
  MsgStrConnectionCanceled = 'Request canceled';
  MsgStrXMLInvalid = 'XML format error.';
  MsgStrXMLFromServerInvalid = 'the XML response that was returned from the server is invalid.';
  MsgStrAtomInvalid = 'Atom format error.';
  MsgStrAtomNoEntryID = 'No entry ID';
  MsgStrAtomEntryIDRequired = 'entry requres an ID';
  MsgStrResponseLocationDidNotReturn = 'Location header did not return.';




implementation


constructor EXMLParseError.Create(XMLError:TXMLErrorInfo);
begin
  FXMLError := XMLError;
  inherited Create(XMLError.Title);
end;

destructor EXMLParseError.Destroy;
begin
  if Assigned(FXMLError) then FXMLError.Free;
  inherited Destroy;
end;

constructor EAtomError.Create(Error:TErrorInfo);
begin
  FError := Error;
  inherited Create(Error.Title);
end;

destructor EAtomError.Destroy;
begin
  if Assigned(FError) then FError.Free;
  inherited Destroy;
end;


{ TAtomTextConstruct }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomTextConstruct.Create;
begin
  TextType:=ttText;
end;

function TAtomCommonAttributes.CalculateUri(baseUri:string; partsUri:string):string;
var
  i : Cardinal;
  BufUri : Array [0..255] of char;
begin
  i:=SizeOf(BufUri);
  if InternetCombineURL(pchar(baseUri),
                        pchar(partsUri),
                        BufUri,
                        i,
                        ICU_BROWSER_MODE) then begin

    result := StrPas(BufUri);
  end else begin
    //TODO: Error?
    //ErrorCode := GetLastError;
    result := partsUri;
  end;

end;

function TAtomCommonAttributes.getBase():string;
var
  strBase:string;
  strParts:string;
  strAbs:string;
begin
  strBase := (self as TAtomCommonAttributes).parentBase;
  strParts := (self as TAtomCommonAttributes).FBase;

  if strBase <> '' then begin
    if strParts <> '' then begin
      strAbs := CalculateUri(strBase,strParts);
    end else begin
      strAbs := strBase;
    end;
  end else begin
    strAbs := strParts;
  end;
  result:=strAbs;
end;

{ TAtomUri }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomUri.Create(BaseUri:string);
begin
  (self as TAtomUri).parentBase := BaseUri;
end;


function TAtomUri.getAbsoluteUri():string;
var
  strBase:string;
  strParts:string;
  strAbs:string;
begin
  strBase := (self as TAtomUri).Base;
  strParts := (self as TAtomUri).Furi;

  if strBase <> '' then begin
    if strParts <> '' then begin
      strAbs := CalculateUri(strBase,strParts);
    end else begin
      strAbs:=strBase;
    end;
  end else begin
    strAbs := strParts;
  end;

  result := strAbs;
end;

constructor TAtomContent.Create;
begin
  Src := TAtomUri.Create(self.Base);
end;

destructor TAtomContent.Destroy;
begin
  Src.Free;
  inherited Destroy;
end;

constructor TAtomPerson.Create;
begin
  Uri := TAtomUri.Create(self.Base);
end;

function TAtomPerson.asXMLObj(documentElementName:string):IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objNode, objAttributeNode: IXMLDOMNode;
begin
  result:=nil;

  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    if documentElementName = '' then exit;
    
    objDocumentNode := Doc.createNode(NODE_ELEMENT,documentElementName,Atom_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      //if objDocumentNode.baseName <> 'person' then exit;
      //could be author,contributor
    
      //@xml:base          use Base property here.
      if (Base <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Base;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      //name
      if objDocumentNode.baseName = 'author' then begin
        objNode := Doc.selectSingleNode('//atom:author/atom:name');
      end else if objDocumentNode.baseName = 'contributor' then begin
        objNode := Doc.selectSingleNode('//atom:contributor/atom:name'); 
      end else begin
        exit;
      end;
      
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'name',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomPerson).Name;
      
      //email
      if objDocumentNode.baseName = 'author' then begin
        objNode := Doc.selectSingleNode('//atom:author/atom:email');
      end else if objDocumentNode.baseName = 'contributor' then begin
        objNode := Doc.selectSingleNode('//atom:contributor/atom:email'); 
      end else begin
        exit;
      end;
      
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'email',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomPerson).Email;

      //uri
      if objDocumentNode.baseName = 'author' then begin
        objNode := Doc.selectSingleNode('//atom:author/atom:uri');
      end else if objDocumentNode.baseName = 'contributor' then begin
        objNode := Doc.selectSingleNode('//atom:contributor/atom:uri'); 
      end else begin
        exit;
      end;
      
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'uri',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomPerson).Uri.Uri;  

      result:=Doc;    
    end;
  end;
  
end;

destructor TAtomPerson.Destroy;
begin
  Uri.Free;
  inherited Destroy;
end;

function TAtomPersonList.Get(Index: Integer): TAtomPerson;
begin
  Result :=  TAtomPerson( inherited Get(Index) );
end;

procedure TAtomPersonList.Put(Index: Integer; const Value: TAtomPerson);
begin
  inherited Put( Index, Value );
end;

destructor TAtomPersonList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;

constructor TAtomLink.Create;
begin
  Href := TAtomUri.Create(self.Base);
end;

function TAtomLink.getXMLObj:IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objAttributeNode: IXMLDOMNode;
begin
  result := nil;

  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;

  Doc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'link',Atom_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'link' then exit;
      if objDocumentNode.namespaceURI <> Atom_NS then exit;

      //@xml:base          use Base property here.
      if (Base <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Base;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;


      objAttributeNode := Doc.documentElement.attributes.getNamedItem('rel');
      if (self as TAtomLink).Rel <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'rel',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).Rel;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('rel',Atom_NS);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('type');
      if (self as TAtomLink).ContentType <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).ContentType;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('type',Atom_NS);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('href');
      if (self as TAtomLink).Href.FUri <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'href',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).Href.FUri;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('href',Atom_NS);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('length');
      if (self as TAtomLink).Length <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'length',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).Length;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('length',Atom_NS);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('title');
      if (self as TAtomLink).Title <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'title',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).Title;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('title',Atom_NS);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('hreflang');
      if (self as TAtomLink).Hreflang <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'hreflang',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomLink).Hreflang;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('hreflang',Atom_NS);
        end;
      end;

      result:=Doc;
    end;

  end;

end;

destructor TAtomLink.Destroy;
begin
  Href.Free;
  inherited Destroy;
end;

{ TAtomLinkList : list of TAtomLink}
function TAtomLinkList.Get(Index: Integer): TAtomLink;
begin
  Result :=  TAtomLink( inherited Get(Index) );
end;

procedure TAtomLinkList.Put(Index: Integer; const Value: TAtomLink);
begin
  inherited Put( Index, Value );
end;

destructor TAtomLinkList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;

constructor TAtomCategory.Create;
begin
  Scheme := TAtomUri.Create(self.Base);
end;

constructor TAtomCategory.Create(fromXML:WideString; baseUri:string);
begin
  //
end;

function TAtomCategory.getXMLObj:IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objAttributeNode: IXMLDOMNode;
begin
  result := nil;

  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'category',Atom_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'category' then exit;
      if objDocumentNode.namespaceURI <> Atom_NS then exit;
    
      //@xml:base          use Base property here.
      if (FBase <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := FBase;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      //Term
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('term');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'term',Atom_NS);
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;
      objAttributeNode.text := (self as TAtomCategory).Term; 


      //CategoryLabel
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('label');
      if (self as TAtomCategory).CategoryLabel <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'label',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomCategory).CategoryLabel;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('label',Atom_NS);
        end;
      end;

      //Scheme
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('scheme');
      if (self as TAtomCategory).Scheme.FUri <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'scheme',Atom_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomCategory).Scheme.FUri;
      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeQualifiedItem('scheme',Atom_NS);
        end;
      end;

      result:=Doc;         
    end;
  end;
  
end;

destructor TAtomCategory.Destroy;
begin
  Scheme.Free;
  inherited Destroy;
end;

constructor TAtomCategories.Create;
begin
  Fixed:=false;
  Scheme:=TAtomUri.Create(self.Base);
  Href:=TAtomUri.Create(self.Base);
  CategoryList:= TAtomCategoryList.Create;
end;

procedure TAtomCategories.setCategories(fromXML:WideString;baseUri:string);
var
  i:integer;
  x:integer;
  objAttributeNode : IXMLDOMNode;

  categoriesDoc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objCategoriesNode:IXMLDOMNode;
  objCategoryList:IXMLDOMNodelist;
begin
  (Self as TAtomCategories).CategoryList.Clear;
  (Self as TAtomCategories).Href.FUri := '';

  (Self as TAtomCategories).FXML:=fromXML;
  (Self as TAtomCategories).parentBase := baseUri;

  categoriesDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  categoriesDoc.async :=  False;
  categoriesDoc.validateOnParse := false;
  categoriesDoc.resolveexternals:=false;
  //categoriesDoc.preserveWhiteSpace := true;

  categoriesDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');
  //X categoriesDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  categoriesDoc.loadXML((Self as TAtomCategories).FXML);

  if (categoriesDoc.readyState = 4) Then begin
    if (categoriesDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=categoriesDoc.parseError.errorCode;
      XMLError.Reason:=categoriesDoc.parseError.reason;
      XMLError.SrcText:=categoriesDoc.parseError.srcText;
      XMLError.Line:=categoriesDoc.parseError.line;
      XMLError.LinePos:=categoriesDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objCategoriesNode:=categoriesDoc.documentElement;
      if objCategoriesNode = nil then exit;
      if objCategoriesNode.baseName <> 'categories' then exit;
      if objCategoriesNode.namespaceURI <> Atom_APP_NS then exit;


      if objCategoriesNode.attributes.getNamedItem('href') <> nil then begin
        (Self as TAtomCategories).Href.FUri := objCategoriesNode.attributes.getNamedItem('href').text;
      end;

      if objCategoriesNode.attributes.getNamedItem('fixed') <> nil then begin
        if objCategoriesNode.attributes.getNamedItem('fixed').text = 'yes' then begin
          (Self as TAtomCategories).Fixed := true;
        end else begin
          (Self as TAtomCategories).Fixed := false;
        end;
      end;

      if objCategoriesNode.attributes.getNamedItem('scheme') <> nil then begin
        (Self as TAtomCategories).Scheme.FUri := objCategoriesNode.attributes.getNamedItem('scheme').text;
      end;

      //category?
      objCategoryList := categoriesDoc.documentElement.selectNodes('atom:category');
      //((Self as TAtomCategories).CategoryList as TAtomCategoryList).setCategories(objCategoryList);

      if (objCategoryList <> nil) then begin

        for i:=0 to objCategoryList.length -1 do begin
          x := (self as TAtomCategories).CategoryList.Add(TAtomCategory.Create);

          (self as TAtomCategories).CategoryList.Items[x].parentBase := (Self as TAtomCategories).Base;
          
          //xml:base and xml:lang
          objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (self as TAtomCategories).CategoryList.Items[x].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (self as TAtomCategories).CategoryList.Items[x].Lang := objAttributeNode.nodeValue;
          end;

          if (objCategoryList.item[i].attributes.getNamedItem('label') <> nil) then begin
            (self as TAtomCategories).CategoryList.Items[x].CategoryLabel := objCategoryList.item[i].attributes.getNamedItem('label').text;
          end;

          (self as TAtomCategories).CategoryList.Items[x].Scheme.parentBase := (self as TAtomCategories).CategoryList.Items[x].Base;
          if (objCategoryList.item[i].attributes.getNamedItem('scheme') <> nil) then begin
            (self as TAtomCategories).CategoryList.Items[x].Scheme.Uri := objCategoryList.item[i].attributes.getNamedItem('scheme').text;
            if (self as TAtomCategories).CategoryList.Items[x].Scheme.Uri = '' then begin
              //scheme iheritance from parent Categories'.
              (self as TAtomCategories).CategoryList.Items[x].Scheme.Uri := (Self as TAtomCategories).Scheme.Uri;
            end;
          end;
          if (objCategoryList.item[i].attributes.getNamedItem('term') <> nil) then begin
            (self as TAtomCategories).CategoryList.Items[x].Term := objCategoryList.item[i].attributes.getNamedItem('term').text;
          end;
          (self as TAtomCategories).CategoryList.Items[x].FXML:=objCategoryList.item[i].xml;
        end;

      end;

    end;
  end;
end;

{
constructor TAtomCategories.Create(fromXML:WideString; baseUri:string);
begin
  Fixed:=false;
  Scheme:=TAtomUri.Create;
  Href:=TAtomUri.Create;
  Categories:= TAtomCategoryList.Create;
  (Self as TAtomCategories).parentBase := baseUri;
  (Self as TAtomCategories).setCategories(fromXML,baseUri);

end;
}

{
function TAtomCategories.getCategories;
begin
  result := FCategories;
end;

procedure TAtomCategories.setCategories(Categories:TAtomCategoryList);
begin
  FCategories := Categories;
end;
}

function TAtomCategories.asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objNodeChild, objAttributeNode: IXMLDOMNode;
  i:integer;
  objNodeList:IXMLDOMNodeList;
  c:integer;
begin
  result := nil;

  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'categories',Atom_APP_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'categories' then exit;
      if objDocumentNode.namespaceURI <> Atom_APP_NS then exit;

      //@xml:base          use Base property here.
      if (FBase <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := FBase;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns:app',XML_NS);
        objAttributeNode.text := Atom_APP_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns:atom',XML_NS);
        objAttributeNode.text := Atom_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      //remove chiled
      objNodeList:= objDocumentNode.selectNodes('atom:category');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          objDocumentNode.removeChild(objNodeList.item[c]);
        end;
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('href');
      if (self as TAtomCategories).Href.FUri <> '' then begin
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'href',Atom_APP_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomCategories).Href.FUri;

        objAttributeNode := Doc.documentElement.attributes.getNamedItem('fixed');
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeNamedItem('fixed');
        end;
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('scheme');
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeNamedItem('scheme');
        end;

      end else begin
        if objAttributeNode <> nil then begin
          objDocumentNode.attributes.removeNamedItem('href');
        end;

        objAttributeNode := Doc.documentElement.attributes.getNamedItem('fixed');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'fixed',Atom_APP_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        if (self as TAtomCategories).Fixed then begin
          objAttributeNode.text := 'yes';
        end else begin
          objAttributeNode.text := 'no';
        end;

        objAttributeNode := Doc.documentElement.attributes.getNamedItem('scheme');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'scheme',Atom_APP_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomCategories).Scheme.FUri;

        if recurse then begin
          if ((self as TAtomCategories).CategoryList.Count > 0) then begin
            for i:=0 to (self as TAtomCategories).CategoryList.Count-1 do begin
              if Assigned((self as TAtomCategories).CategoryList[i]) then begin
                //objDocumentNode.appendChild();
                objNodeChild := (self as TAtomCategories).CategoryList[i].asXMLObj.documentElement;
                if Assigned(objNodeChild) then begin
                  objDocumentNode.appendChild(objNodeChild);
                end;
              end;
            end;
          end;
        end;


      end;


      result:=Doc;
    end;
  end;
  
end;

destructor TAtomCategories.Destroy;
begin
  CategoryList.Free;
  Scheme.Free;
  Href.Free;
  inherited Destroy;
end;

{
procedure TAtomCategoryList.setCategories(objCategoryList:IXMLDOMNodelist);
var
  i:integer;
  x:integer;
  objAttributeNode : IXMLDOMNode;
begin

  if (objCategoryList <> nil) then begin
    for i:=0 to objCategoryList.length -1 do begin
      x := (Self as TAtomCategoryList).Add(TAtomCategory.Create);

      //(Self as TAtomCategoryList).Items[x].parentBase := baseUri;

      //xml:base and xml:lang
      objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:base');
      if objAttributeNode <> nil then begin
        (Self as TAtomCategoryList).Items[x].Base := objAttributeNode.nodeValue;
      end;
      objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:lang');
      if objAttributeNode <> nil then begin
        (Self as TAtomCategoryList).Items[x].Lang := objAttributeNode.nodeValue;
      end;

      if (objCategoryList.item[i].attributes.getNamedItem('label') <> nil) then begin
        (Self as TAtomCategoryList).Items[x].CategoryLabel := objCategoryList.item[i].attributes.getNamedItem('label').text;
      end;
      if (objCategoryList.item[i].attributes.getNamedItem('scheme') <> nil) then begin
        (Self as TAtomCategoryList).Items[x].Scheme.Uri := objCategoryList.item[i].attributes.getNamedItem('scheme').text;
      end;
      if (objCategoryList.item[i].attributes.getNamedItem('term') <> nil) then begin
        (Self as TAtomCategoryList).Items[x].Term := objCategoryList.item[i].attributes.getNamedItem('term').text;
      end;
      (Self as TAtomCategoryList).Items[x].FXML:=objCategoryList.item[i].xml;
    end;
  end;
end;
}

function TAtomCategoryList.Get(Index: Integer): TAtomCategory;
begin
  Result :=  TAtomCategory( inherited Get(Index) );
end;

procedure TAtomCategoryList.Put(Index: Integer; const Value: TAtomCategory);
begin
  inherited Put( Index, Value );
end;

destructor TAtomCategoryList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do begin
    Self.Items[i].Free;
  end;
  inherited Destroy;
end;


{ TAtomEntry }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomEntry.Create(baseUri:string = '');
begin
  Title := TAtomTextConstruct.Create;
  Links := TAtomLinkList.Create;
  Authors := TAtomPersonList.Create;
  Contributors := TAtomPersonList.Create;
  Content := TAtomContent.Create;
  Summary := TAtomTextConstruct.Create;
  Categories := TAtomCategoryList.Create;
  PubControl := TAtomPubControl.Create;
  Rights := TAtomTextConstruct.Create;
  Source := '';

  (Self as TAtomEntry).parentBase := baseUri;
end;

constructor TAtomEntry.Create(fromXML:WideString; baseUri:string);
var
  entryDoc : IXMLDOMDocument2;
  objNode:IXMLDOMNode;
  objEntryNode:IXMLDOMNode;
  objList:IXMLDOMNodelist;
  objAuthorList:IXMLDOMNodelist;
  objContributerList:IXMLDOMNodelist;
  objCategoryList:IXMLDOMNodelist;
  i:integer;
  x:integer;
  objAttributeNode : IXMLDOMNode;
  XMLError:TXMLErrorInfo;
  objTmpNodeList:IXMLDOMNodelist;
  c:integer;
  {$IFDEF AtomStrictMode}
  Error:TErrorInfo;
  {$ENDIF}
begin
  Title := TAtomTextConstruct.Create;
  Links := TAtomLinkList.Create;
  Authors := TAtomPersonList.Create;
  Contributors := TAtomPersonList.Create;
  Content := TAtomContent.Create;
  Summary := TAtomTextConstruct.Create;
  Categories := TAtomCategoryList.Create;
  PubControl := TAtomPubControl.Create;
  Rights := TAtomTextConstruct.Create;
  Source := '';

  //keep original. this preserves foregin markups.
  (Self as TAtomEntry).FXML := fromXML;

  //build TAtomEntry From XML, sets values etc. This creates TAtomEntry from APP Entry XML Document

  entryDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  entryDoc.async :=  False;
  entryDoc.validateOnParse := false;
  entryDoc.resolveexternals:=false;
  //entryDoc.preserveWhiteSpace := true;

  entryDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  entryDoc.loadXML(fromXML);

  if (entryDoc.readyState = 4) Then begin
    if (entryDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=entryDoc.parseError.errorCode;
      XMLError.Reason:=entryDoc.parseError.reason;
      XMLError.SrcText:=entryDoc.parseError.srcText;
      XMLError.Line:=entryDoc.parseError.line;
      XMLError.LinePos:=entryDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objEntryNode:=entryDoc.documentElement;
      if objEntryNode = nil then exit;
      //if objEntryNode.baseName <> 'entry' then exit;
      {$IFDEF AtomStrictMode}
      if objEntryNode.baseName <> 'entry' then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := 'EntryNode.baseName != "entry".';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}
      //if objEntryNode.namespaceURI <> Atom_NS then exit;
      {$IFDEF AtomStrictMode}
      if objEntryNode.namespaceURI <> Atom_NS then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := 'EntryNode.namespaceURI != Atom_NS.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

      (Self as TAtomEntry).parentBase := baseUri;

      //xml:base and xml:lang
      objAttributeNode := objEntryNode.attributes.getNamedItem('xml:base');
      if objAttributeNode <> nil then begin
        (Self as TAtomEntry).Base := objAttributeNode.nodeValue;
      end;
      objAttributeNode := objEntryNode.attributes.getNamedItem('xml:lang');
      if objAttributeNode <> nil then begin
        (Self as TAtomEntry).Lang := objAttributeNode.nodeValue;
      end;

      //id
      objNode := entryDoc.documentElement.selectSingleNode('atom:id');
      if objNode <> nil then begin
        (Self as TAtomEntry).Id := objNode.text;
      end;
      {$IFDEF AtomStrictMode}
      if (Self as TAtomEntry).Id = '' then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := '//atom:entry/atom:id is empty.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

      //title
      objNode := entryDoc.documentElement.selectSingleNode('atom:title');
      if objNode <> nil then begin
        (Self as TAtomEntry).Title.Text := objNode.text;
        if objNode.attributes.getNamedItem('type') <> nil then begin
          if objNode.attributes.getNamedItem('type').text = 'text' then begin
            (Self as TAtomEntry).Title.TextType := ttText;
          end else if (objNode.attributes.getNamedItem('type').text = 'html') then begin
            (Self as TAtomEntry).Title.TextType := ttHTML;
          end else if (objNode.attributes.getNamedItem('type').text = 'xhtml') then begin
            (Self as TAtomEntry).Title.TextType := ttXHTML;
            if objNode.hasChildNodes then begin
              if objNode.firstChild.baseName = 'div' then begin
                (Self as TAtomEntry).Title.Text := objNode.firstChild.xml;
              end;
            end;
          end;
        end;
            
        //xml:base and xml:lang
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Title.Base := objAttributeNode.nodeValue;
        end;
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Title.Lang := objAttributeNode.nodeValue;
        end;
        (Self as TAtomEntry).Title.parentBase := (Self as TAtomEntry).Base;
      end;

      //link
      objList := entryDoc.documentElement.selectNodes('atom:link');
      if (objList <> nil) then begin
        for i:=0 to objList.length-1 do begin
          x := (Self as TAtomEntry).Links.Add(TAtomLink.Create);
          //xml:base and xml:lang
          objAttributeNode := objList.item[i].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objList.item[i].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Lang := objAttributeNode.nodeValue;
          end;
          (Self as TAtomEntry).Links.Items[x].parentBase := (Self as TAtomEntry).Base;

          if objList.item[i].attributes.getNamedItem('rel') <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Rel := objList.item[i].attributes.getNamedItem('rel').text;
          end;
          if objList.item[i].attributes.getNamedItem('type') <> nil then begin
            (Self as TAtomEntry).Links.Items[x].ContentType := objList.item[i].attributes.getNamedItem('type').text;
          end;
          if objList.item[i].attributes.getNamedItem('href') <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Href.parentBase := (Self as TAtomEntry).Links.Items[x].Base;
            (Self as TAtomEntry).Links.Items[x].Href.Uri := objList.item[i].attributes.getNamedItem('href').text;
          end;
          if objList.item[i].attributes.getNamedItem('title') <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Title := objList.item[i].attributes.getNamedItem('title').text;
          end;
          if objList.item[i].attributes.getNamedItem('hreflang') <> nil then begin
            (Self as TAtomEntry).Links.Items[x].Hreflang := objList.item[i].attributes.getNamedItem('hreflang').text;
          end;

          if self is TAtomEntryExt then begin
            if objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':etag') <> nil then begin
              (Self as TAtomEntryExt).Etag := objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':etag').text;
            end;
            if objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':etagMedia') <> nil then begin
              (Self as TAtomEntryExt).EtagMedia := objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':etagMedia').text;
            end;
            {
            if objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':status') <> nil then begin
              if objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':status').text = 'new' then begin
                (Self as TAtomEntryExt).Status := esNew;
              end else if objList.item[i].attributes.getNamedItem(Witha_NS_Prefix+':status').text = 'updated' then begin
                (Self as TAtomEntryExt).Status := esUpdated;
              end else begin
                (Self as TAtomEntryExt).Status := esNone;
              end;
            end else begin
              (Self as TAtomEntryExt).Status := esNone;
            end;
            }
          end;

          (Self as TAtomEntry).Links.Items[x].FXML:=objList.item[i].xml;
        end;

      end;

      //author
      objAuthorList := entryDoc.documentElement.selectNodes('atom:author');
      if (objAuthorList <> nil) then begin
        for i:=0 to objAuthorList.length -1 do begin
          x := (Self as TAtomEntry).Authors.Add(TAtomPerson.Create);

          //xml:base and xml:lang
          objAttributeNode := objAuthorList.item[i].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Authors.Items[x].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objAuthorList.item[i].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Authors.Items[x].Lang := objAttributeNode.nodeValue;
          end;
          (Self as TAtomEntry).Authors.Items[x].parentBase := (Self as TAtomEntry).Base;

          if (objAuthorList.item[i].selectSingleNode('atom:name') <> nil) then begin
            (Self as TAtomEntry).Authors[x].Name := objAuthorList.item[i].selectSingleNode('atom:name').text;
          end;
          if (objAuthorList.item[i].selectSingleNode('atom:uri') <> nil) then begin
            (Self as TAtomEntry).Authors[x].Uri.parentBase := (Self as TAtomEntry).Authors[x].Base;
            (Self as TAtomEntry).Authors[x].Uri.Uri := objAuthorList.item[i].selectSingleNode('atom:uri').text;
          end;
          if (objAuthorList.item[i].selectSingleNode('atom:email') <> nil) then begin
            (Self as TAtomEntry).Authors[x].Email := objAuthorList.item[i].selectSingleNode('atom:email').text;
          end;
          (Self as TAtomEntry).Authors[x].FXML:=objAuthorList.item[i].xml;
        end;
      end;

      //contributor
      objContributerList := entryDoc.documentElement.selectNodes('atom:contributer');
      if (objContributerList <> nil) then begin
        for i:=0 to objContributerList.length -1 do begin
          x := (Self as TAtomEntry).Contributors.Add(TAtomPerson.Create);

          //xml:base and xml:lang
          objAttributeNode := objContributerList.item[i].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Contributors.Items[x].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objContributerList.item[i].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Contributors.Items[x].Lang := objAttributeNode.nodeValue;
          end;
          (Self as TAtomEntry).Contributors.Items[x].parentBase := (Self as TAtomEntry).Base;

          if (objContributerList.item[i].selectSingleNode('atom:name') <> nil) then begin
            (Self as TAtomEntry).Contributors[x].Name := objContributerList.item[i].selectSingleNode('atom:name').text;
          end;
          if (objContributerList.item[i].selectSingleNode('atom:uri') <> nil) then begin
            (Self as TAtomEntry).Contributors[x].Uri.parentBase := (Self as TAtomEntry).Contributors[x].Base;
            (Self as TAtomEntry).Contributors[x].Uri.Uri := objContributerList.item[i].selectSingleNode('atom:uri').text;
          end;
          if (objContributerList.item[i].selectSingleNode('atom:email') <> nil) then begin
            (Self as TAtomEntry).Contributors[x].Email := objContributerList.item[i].selectSingleNode('atom:email').text;
          end;
          (Self as TAtomEntry).Contributors[x].FXML:=objContributerList.item[i].xml;
        end;
      end;

      //category
      objCategoryList := entryDoc.documentElement.selectNodes('atom:category');
      //(Self as TAtomEntry).Categories.setCategories(objCategoryList);
      if (objCategoryList <> nil) then begin
        for i:=0 to objCategoryList.length -1 do begin
          x := (Self as TAtomEntry).Categories.Add(TAtomCategory.Create);

          //(Self as TAtomCategoryList).Items[x].parentBase := baseUri;

          //xml:base and xml:lang
          objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Categories.Items[x].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objCategoryList.item[i].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (Self as TAtomEntry).Categories.Items[x].Lang := objAttributeNode.nodeValue;
          end;

          if (objCategoryList.item[i].attributes.getNamedItem('label') <> nil) then begin
            (Self as TAtomEntry).Categories.Items[x].CategoryLabel := objCategoryList.item[i].attributes.getNamedItem('label').text;
          end;
          if (objCategoryList.item[i].attributes.getNamedItem('scheme') <> nil) then begin
            (Self as TAtomEntry).Categories.Items[x].Scheme.Uri := objCategoryList.item[i].attributes.getNamedItem('scheme').text;
          end;
          if (objCategoryList.item[i].attributes.getNamedItem('term') <> nil) then begin
            (Self as TAtomEntry).Categories.Items[x].Term := objCategoryList.item[i].attributes.getNamedItem('term').text;
          end;
          (Self as TAtomEntry).Categories.Items[x].FXML:=objCategoryList.item[i].xml;
        end;
      end;
      
      //summary
      objNode := entryDoc.documentElement.selectSingleNode('atom:summary');
      if objNode <> nil then begin

        (Self as TAtomEntry).Summary.Text := objNode.text;
        if objNode.attributes.getNamedItem('type') <> nil then begin
          if (objNode.attributes.getNamedItem('type').text = 'text') then begin
            (Self as TAtomEntry).Summary.TextType := ttText;
          end else if (objNode.attributes.getNamedItem('type').text = 'html') then begin
            (Self as TAtomEntry).Summary.TextType := ttHtml;
          end else if (objNode.attributes.getNamedItem('type').text = 'xhtml') then begin
            (Self as TAtomEntry).Summary.TextType := ttXhtml;
            {
            if objNode.hasChildNodes then begin
              if objNode.firstChild.baseName = 'div' then begin
                (Self as TAtomEntry).Summary.Text := objNode.firstChild.xml;
              end;
            end;
            }
            //TODO: need to look up the spec again.
            //(Self as TAtomEntry).Summary.Text := ''; //init
            if objNode.hasChildNodes then begin
              objTmpNodeList := objNode.childNodes;
              for c:=0 to objTmpNodeList.length-1 do begin
                if objTmpNodeList.item[c].nodeType = NODE_ELEMENT then begin
                  if objTmpNodeList.item[c].baseName = 'div' then begin
                    (Self as TAtomEntry).Summary.Text := objTmpNodeList.item[c].xml;
                  end;
                end;
              end;
            end else begin
              //TODO: error event
              //invalid. must have one div
            end;
          end;
        end;

        //xml:lang and xml:base
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Summary.Base := objAttributeNode.nodeValue;
        end;
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Summary.Lang := objAttributeNode.nodeValue;
        end;
        (Self as TAtomEntry).Summary.parentBase := (Self as TAtomEntry).Base;
      end;

      //content
      objNode := entryDoc.documentElement.selectSingleNode('atom:content');
      if objNode <> nil then begin
        (Self as TAtomEntry).Content.Text := objNode.text;
        //xml:lang and xml:base
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Content.Base := objAttributeNode.nodeValue;
        end;
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if objAttributeNode <> nil then begin
          (Self as TAtomEntry).Content.Lang := objAttributeNode.nodeValue;
        end;
        (Self as TAtomEntry).Content.parentBase := (Self as TAtomEntry).Base;
        if objNode.attributes.getNamedItem('type') <> nil then begin
          (Self as TAtomEntry).Content.ContentType := objNode.attributes.getNamedItem('type').text;
          //unlike atomTextConstruContentct, choices are "text|html|xhtml|(any mime-type)".
        end;
        if objNode.attributes.getNamedItem('src') <> nil then begin
          (Self as TAtomEntry).Content.Src.parentBase := (Self as TAtomEntry).Content.Base;
          (Self as TAtomEntry).Content.Src.Uri := objNode.attributes.getNamedItem('src').nodeValue;
          (Self as TAtomEntry).Content.Text := '';
        end else begin
          if (Self as TAtomEntry).Content.ContentType = 'text' then begin
            (Self as TAtomEntry).Content.Text := objNode.text;
          end else if ((Self as TAtomEntry).Content.ContentType = 'html') then begin
            //TODO: need to look up the spec again.
            //@type=html does not have a child, but cdata and text node also appear as a child. just treat as text.
            (Self as TAtomEntry).Content.Text := objNode.text;//GetChildXMLString(objNode);
          end else if ((Self as TAtomEntry).Content.ContentType = 'xhtml') then begin
            //TODO: need to look up the spec again.
            (Self as TAtomEntry).Content.Text := ''; //init
            if objNode.hasChildNodes then begin
              objTmpNodeList := objNode.childNodes;
              for c:=0 to objTmpNodeList.length-1 do begin
                if objTmpNodeList.item[c].nodeType = NODE_ELEMENT then begin
                  if objTmpNodeList.item[c].baseName = 'div' then begin
                    (Self as TAtomEntry).Content.Text := objTmpNodeList.item[c].xml;
                  end;
                end;
              end;
            end else begin
              //TODO: error event
              //invalid. must have one div
            end;
          end else begin
            //TODO: ?? base64 etc
            //unknown media type,... or.. "src" must point to the actual content.
            (Self as TAtomEntry).Content.Text := objNode.text;
          end;
        end;

      end;

      //published
      objNode := entryDoc.documentElement.selectSingleNode('atom:published');
      if objNode <> nil then begin
        if objNode.text <> '' then begin

          (Self as TAtomEntry).DateTimePublished := XMLTimeToDateTime(objNode.text,false);

        end;
      end;

      //updated
      objNode := entryDoc.documentElement.selectSingleNode('atom:updated');
      if objNode <> nil then begin
        if objNode.text <> '' then begin
          (Self as TAtomEntry).DateTimeUpdated := XMLTimeToDateTime(objNode.text,false);
        end;
      end;
      {$IFDEF AtomStrictMode}
      if YearOf((Self as TAtomEntry).DateTimeUpdated) = 1899 then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;//'Atom Format Error.';
        Error.Description := '//atom:entry/atom:updated is empty.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

      //edited
      objNode := entryDoc.documentElement.selectSingleNode(Atom_APP_NS_Prefix+':edited');
      if objNode <> nil then begin
        if objNode.text <> '' then begin
          (Self as TAtomEntry).DateTimeEdited := XMLTimeToDateTime(objNode.text,false);
        end;
      end;

      //pubControl:draft

      objNode := entryDoc.documentElement.selectSingleNode('//atom:entry/'+Atom_PubControl_NS_Prefix+':control'+'/'+Atom_PubControl_NS_Prefix+':draft');
      if objNode <> nil then begin
        if objNode.text <> 'yes' then begin
          (Self as TAtomEntry).PubControl.PubDraft := false;
        end else begin
          (Self as TAtomEntry).PubControl.PubDraft := true;
        end;
      end;

      //rights
      objNode := entryDoc.documentElement.selectSingleNode('atom:rights');
      if objNode <> nil then begin
        (Self as TAtomEntry).Rights.Text := objNode.text;
      end;

      //source
      objNode := entryDoc.documentElement.selectSingleNode('atom:source');
      if objNode <> nil then begin
        (Self as TAtomEntry).Source := objNode.text;
      end;



      {objNode := entryDoc.documentElement.selectSingleNode(Atom_PubControl_NS_Prefix+':draft');
      if objNode <> nil then begin
        if objNode.text <> 'yes' then begin
          (Self as TAtomEntry).PubControl.PubDraft := false;
        end else begin
          (Self as TAtomEntry).PubControl.PubDraft := true;
        end;
      end;
      }

    end;
  end;
end;

destructor TAtomEntry.Destroy;
begin
  Title.Free;
  Links.Free;
  Authors.Free;
  Contributors.Free;
  Content.Free;
  Summary.Free;
  Categories.Free;
  Rights.Free;
  PubControl.Free;

  inherited Destroy;
end;

function TAtomEntry.getEditUri():string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomEntry(self).Links.Count-1 do begin
    if UpperCase(TAtomEntry(self).Links.Items[i].Rel) = 'EDIT' then begin
      //result := TAtomEntry(self).Links.Items[i].Href;
      if Assigned(TAtomEntry(self).Links.Items[i].Href) then begin
        result := TAtomEntry(self).Links.Items[i].Href.Uri;
      end;
    end;
  end;
end;

procedure TAtomEntry.setEditUri(strUri:string);
var
  i:integer;
  hasRelEdit:boolean;
begin
  hasRelEdit:=false;
  for i:=0 to TAtomEntry(self).Links.Count-1 do begin
    if UpperCase(TAtomEntry(self).Links.items[i].Rel) = 'EDIT' then begin
      hasRelEdit := true;
      if strUri <> '' then begin
        if Assigned(TAtomEntry(self).Links.items[i].Href) then begin
          TAtomEntry(self).Links.items[i].Href.Uri := strUri;
        end;
      end;
    end;
  end;

  if not hasRelEdit then begin
    i := TAtomEntry(self).Links.Add(TAtomLink.Create);
    TAtomEntry(self).Links.Items[i].Rel := 'edit';
    TAtomEntry(self).Links.Items[i].Href.parentBase := TAtomEntry(self).Base;
    TAtomEntry(self).Links.Items[i].Href.Uri := strUri;
  end;
end;

function TAtomEntry.getEditMediaUri():string;
var
  i:integer;
begin
  result:='';
  if TAtomEntry(self).Links.Count <= 0 then exit;
  for i:=0 to TAtomEntry(self).Links.Count-1 do begin
    if UpperCase(TAtomEntry(self).Links.Items[i].Rel) = 'EDIT-MEDIA' then begin

      if Assigned(TAtomEntry(self).Links.Items[i].Href) then begin
        if TAtomEntry(self).Links.Items[i].Href.FUri <> '' then begin
          //return absolute uri
          result := TAtomEntry(self).Links.Items[i].Href.Uri;
        end else begin
          //only base uri exists. no href exits. so retun nothing
          result:='';
        end;

      end;
    end;
  end;
end;

function TAtomEntry.getXMLObj:IXMLDOMDocument2;
var
  entryDoc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objNode, objEntryNode, objAttributeNode, objControlNode: IXMLDOMNode;
  i:integer;
  objNodeList:IXMLDOMNodelist;
  c:integer;
  objTmpNode:IXMLDOMNode;
begin
  result := nil;
  entryDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  entryDoc.async :=  False;
  entryDoc.validateOnParse := false;
  entryDoc.resolveexternals:=false;
  //entryDoc.preserveWhiteSpace := true;

  entryDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if (self as TAtomEntry).FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    entryDoc.loadXML(FXML);
  end else begin
    //entry
    objEntryNode := entryDoc.createNode(NODE_ELEMENT,'entry',Atom_NS);
    entryDoc.appendChild(objEntryNode);
  end;

  if (entryDoc.readyState = 4) Then begin
    if (entryDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=entryDoc.parseError.errorCode;
      XMLError.Reason:=entryDoc.parseError.reason;
      XMLError.SrcText:=entryDoc.parseError.srcText;
      XMLError.Line:=entryDoc.parseError.line;
      XMLError.LinePos:=entryDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objEntryNode := entryDoc.documentElement;
      if objEntryNode.baseName <> 'entry' then exit;

      //@xml:base          use Base property here.
      if ((self as TAtomEntry).FBase <> '') then begin
        objAttributeNode := entryDoc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objEntryNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomEntry).FBase;
      end else begin
        objAttributeNode := entryDoc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objEntryNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if ((self as TAtomEntry).Lang <> '') then begin
        objAttributeNode := entryDoc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objEntryNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomEntry).Lang;
      end else begin
        objAttributeNode := entryDoc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objEntryNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := entryDoc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_NS;
        objEntryNode.attributes.setNamedItem(objAttributeNode);
      end;

      //title
      if Assigned((self as TAtomEntry).Title) then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/atom:title');
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,'title',Atom_NS);
          objEntryNode.appendChild(objNode);
        end;

        //@xml:base
        if ((self as TAtomEntry).Title.FBase <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).FBase;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
        end;

        //@xml:lang
        if ((self as TAtomEntry).Title.Lang <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).Lang;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
        end;

        objNode.text := (self as TAtomEntry).Title.Text;
        objAttributeNode := objNode.attributes.getNamedItem('type');
        if objAttributeNode = nil then begin
          objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        case (self as TAtomEntry).Title.TextType of
          ttText:objAttributeNode.text := 'text';
          ttHtml:objAttributeNode.text := 'html';
          ttXhtml:objAttributeNode.text := 'xhtml';
        end;
      end;

      //id
      if ((self as TAtomEntry).Id = '') then begin
        //id is required
        (self as TAtomEntry).Id := 'urn:uuid:'+CreateClassID; //create uuid or guid
      end;
      objNode := entryDoc.selectSingleNode('//atom:entry/atom:id');
      if objNode = nil then begin
        objNode := entryDoc.createNode(NODE_ELEMENT,'id',Atom_NS);
        objEntryNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomEntry).Id;

      //authors

      //delete authors
      objNodeList:= entryDoc.selectNodes('//atom:entry/atom:author');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          entryDoc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomEntry).Authors.Count > 0) then begin
        for i:=0 to (self as TAtomEntry).Authors.Count-1 do begin
          if Assigned((self as TAtomEntry).Authors[i]) then begin
             objEntryNode.appendChild((self as TAtomEntry).Authors[i].asXMLObj('author').documentElement);

          end;
        end;
      end;

      //contributors

      //delete contributors
      objNodeList:= entryDoc.selectNodes('//atom:entry/atom:contributor');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          entryDoc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomEntry).Contributors.Count > 0) then begin
        for i:=0 to (self as TAtomEntry).Contributors.Count-1 do begin
          if Assigned((self as TAtomEntry).Contributors[i]) then begin
            objEntryNode.appendChild((self as TAtomEntry).Contributors[i].asXMLObj('contributor').documentElement);

          end;
        end;
      end;

      //Links!

      //delete link
      objNodeList:= entryDoc.selectNodes('//atom:entry/atom:link');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          entryDoc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomEntry).Links.Count > 0) then begin
        for i:=0 to (self as TAtomEntry).Links.Count-1 do begin
          if Assigned((self as TAtomEntry).Links[i]) then begin
            objNode := objEntryNode.appendChild((self as TAtomEntry).Links[i].asXMLObj.documentElement);
            //

            //with Witha_NS
            if uppercase((self as TAtomEntry).Links[i].Rel) = uppercase('edit') then begin

              if self is TAtomEntryExt then begin
                if (self as TAtomEntryExt).Etag <> '' then begin

                  objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':etag');
                  if objAttributeNode = nil then begin
                    objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,Witha_NS_Prefix+':etag',Witha_NS);
                    objNode.attributes.setNamedItem(objAttributeNode);
                  end;
                  objAttributeNode.text := (self as TAtomEntryExt).Etag;

                end;
                {
                objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':status');
                if objAttributeNode = nil then begin
                  objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,Witha_NS_Prefix+':status',Witha_NS);
                  objNode.attributes.setNamedItem(objAttributeNode);
                end;
                if (self as TAtomEntryExt).Status = esNew then begin
                  objAttributeNode.text := 'new';
                end else if (self as TAtomEntryExt).Status = esUpdated then begin
                  objAttributeNode.text := 'udapted';
                end else begin
                  objAttributeNode.text := '';
                end;
                }
              end else begin
                objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':etag');
                if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem(Witha_NS_Prefix+':etag');
              end;
            end else if uppercase((self as TAtomEntry).Links[i].Rel) = uppercase('edit-media') then begin

              if self is TAtomEntryExt then begin
                if (self as TAtomEntryExt).EtagMedia <> '' then begin

                  objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':etagMedia');
                  if objAttributeNode = nil then begin
                    objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,Witha_NS_Prefix+':etagMedia',Witha_NS);
                    objNode.attributes.setNamedItem(objAttributeNode);
                  end;
                  objAttributeNode.text := (self as TAtomEntryExt).EtagMedia;

                end;
                {
                objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':status');
                if objAttributeNode = nil then begin
                  objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,Witha_NS_Prefix+':status',Witha_NS);
                  objNode.attributes.setNamedItem(objAttributeNode);
                end;
                if (self as TAtomEntryExt).Status = esNew then begin
                  objAttributeNode.text := 'new';
                end else if (self as TAtomEntryExt).Status = esUpdated then begin
                  objAttributeNode.text := 'udapted';
                end else begin
                  objAttributeNode.text := '';
                end;
                }
              end else begin
                objAttributeNode := objNode.attributes.getNamedItem(Witha_NS_Prefix+':etagMedia');
                if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem(Witha_NS_Prefix+':etagMedia');
              end;

            end;



            //
          end;
        end;
      end;

      //updated
      if YearOf((self as TAtomEntry).DateTimeUpdated) > 1899 then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/atom:updated');
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,'updated',Atom_NS);
          objEntryNode.appendChild(objNode);
        end;
        objNode.text := DateTimeToXMLTime((self as TAtomEntry).DateTimeUpdated,true);
      end;

      //published
      if YearOf((self as TAtomEntry).DateTimePublished) > 1899 then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/atom:published');
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,'published',Atom_NS);
          objEntryNode.appendChild(objNode);
        end;
        objNode.text := DateTimeToXMLTime((self as TAtomEntry).DateTimePublished,true);
      end;

      //edited
      if YearOf((self as TAtomEntry).DateTimePublished) > 1899 then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/'+Atom_APP_NS_Prefix+':edited');
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,Atom_APP_NS_Prefix+':edited',Atom_APP_NS);
          objEntryNode.appendChild(objNode);
        end;
        objNode.text := DateTimeToXMLTime((self as TAtomEntry).DateTimeEdited,true);
      end;


      //categories

      //delete categories
      objNodeList:= entryDoc.selectNodes('//atom:entry/atom:category');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          entryDoc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomEntry).Categories.Count > 0) then begin
        for i:=0 to (self as TAtomEntry).Categories.Count-1 do begin
          if Assigned((self as TAtomEntry).Categories[i]) then begin
            objEntryNode.appendChild((self as TAtomEntry).Categories[i].asXMLObj.documentElement);

          end;
        end;
      end;

      //summary
      if Assigned((self as TAtomEntry).Summary) then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/atom:summary');
        //if (self as TAtomEntry).Summary.Text <> '' then begin
          if objNode = nil then begin
            objNode := entryDoc.createNode(NODE_ELEMENT,'summary',Atom_NS);
            objEntryNode.appendChild(objNode);
          end;
          //objNode.text := (self as TAtomEntry).Summary.Text;

          if (self as TAtomEntry).Summary.TextType = ttXhtml then begin
            //use tmpDoc and load text as xml.
            objTmpNode := GetObjNodeFromXML((self as TAtomEntry).Summary.Text);
            if Assigned(objTmpNode) then begin
              objNode.text:='';
              objNode.appendChild(objTmpNode);
            end else begin
              //xml parser error occured. fall back to html.
              (self as TAtomEntry).Summary.TextType := ttHtml;
              objNode.text := (self as TAtomEntry).Summary.Text;
            end;
          end else begin
            objNode.text := (self as TAtomEntry).Summary.Text;
          end;
          //

          objAttributeNode := objNode.attributes.getNamedItem('type');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          case (self as TAtomEntry).Summary.TextType of
            ttText:objAttributeNode.text := 'text';
            ttHtml:objAttributeNode.text := 'html';
            ttXhtml:objAttributeNode.text := 'xhtml';
          end;

          //@xml:base
          if ((self as TAtomEntry).Summary.FBase <> '') then begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:base');
            if objAttributeNode = nil then begin
              objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
              objNode.attributes.setNamedItem(objAttributeNode);
            end;
            objAttributeNode.text := (self as TAtomEntry).Summary.FBase;
          end else begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:base');
            if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
          end;
          //@xml:lang
          if ((self as TAtomEntry).Summary.Lang <> '') then begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
            if objAttributeNode = nil then begin
              objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
              objNode.attributes.setNamedItem(objAttributeNode);
            end;
            objAttributeNode.text := (self as TAtomEntry).Summary.Lang;
          end else begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
            if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
          end;
        //end else begin
        //  if objNode <> nil then begin
        //    if objNode.attributes.length = 0 then begin
        //      // Summary is empty and there IS some Content, so safe to remove this empty Summary.
        //      if ((self as TAtomEntry).Content.Text <> '') or ((self as TAtomEntry).Content.Src.Uri <> '') then begin
        //        //entryDoc.removeChild(objNode);
        //      end;
        //    end;
        //  end;
        //end;
      end;


      //if content is conpletely (including src,text,) empty and summary is there, remove atom:content tag.

      //content
      if Assigned((self as TAtomEntry).Content) then begin
        objNode := entryDoc.selectSingleNode('//atom:entry/atom:content');
        if ((self as TAtomEntry).Content.Text <> '') or ((self as TAtomEntry).Content.Src.Uri <> '') then begin
          if objNode = nil then begin
            objNode := entryDoc.createNode(NODE_ELEMENT,'content',Atom_NS);
            objEntryNode.appendChild(objNode);
          end;

          //objNode.text := (self as TAtomEntry).Content.Text;

          if (self as TAtomEntry).Content.ContentType = 'xhtml' then begin
            //use tmpDoc and load text as xml.
            objTmpNode := GetObjNodeFromXML((self as TAtomEntry).Content.Text);
            if Assigned(objTmpNode) then begin
              objNode.text:='';
              objNode.appendChild(objTmpNode);
            end else begin
              //xml parser error occured. fall back to html.
              (self as TAtomEntry).Content.ContentType := 'html';
              objNode.text := (self as TAtomEntry).Content.Text;
            end;
          end else begin
            objNode.text := (self as TAtomEntry).Content.Text;
          end;

          if ((self as TAtomEntry).Content.Src.FUri <> '') then begin
          //if Assigned((self as TAtomEntry).Content.Src) then begin
            objAttributeNode := objNode.attributes.getNamedItem('src');
            if objAttributeNode = nil then begin
              objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'src',Atom_NS);
              objNode.attributes.setNamedItem(objAttributeNode);
            end;
            objAttributeNode.text := (self as TAtomEntry).Content.Src.FUri;

            //If the "src" attribute is present, atom:content MUST be empty. (draft-08 section 4.1.3.2)
            objNode.text := '';

            //type SHOULD be MIME type if src is present
            if ((self as TAtomEntry).Content.ContentType <> '') then begin
              if (((self as TAtomEntry).Content.ContentType <> 'text') and ((self as TAtomEntry).Content.ContentType <> 'html') and ((self as TAtomEntry).Content.ContentType <> 'xhtml')) then begin
                objAttributeNode := objNode.attributes.getNamedItem('type');
                if objAttributeNode = nil then begin
                  objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
                  objNode.attributes.setNamedItem(objAttributeNode);
                end;
                objAttributeNode.text := (self as TAtomEntry).Content.ContentType;
              end else begin
                objAttributeNode := objNode.attributes.getNamedItem('type');
                if Assigned(objAttributeNode) then objNode.attributes.removeQualifiedItem('type',Atom_NS);
              end;
            end;
          end else begin
            //type
            if ((self as TAtomEntry).Content.ContentType <> '') then begin
              objAttributeNode := objNode.attributes.getNamedItem('type');
              if objAttributeNode = nil then begin
                objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
                objNode.attributes.setNamedItem(objAttributeNode);
              end;
              objAttributeNode.text := (self as TAtomEntry).Content.ContentType;
            end else begin
              objAttributeNode := objNode.attributes.getNamedItem('type');
              if Assigned(objAttributeNode) then objNode.attributes.removeQualifiedItem('type',Atom_NS);
            end;
          end;

          //@xml:base
          if ((self as TAtomEntry).Content.FBase <> '') then begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:base');
            if objAttributeNode = nil then begin
              objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
              objNode.attributes.setNamedItem(objAttributeNode);
            end;
            objAttributeNode.text := (self as TAtomEntry).Content.FBase;
          end;
          //@xml:lang
          if ((self as TAtomEntry).Content.Lang <> '') then begin
            objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
            if objAttributeNode = nil then begin
              objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
              objNode.attributes.setNamedItem(objAttributeNode);
            end;
            objAttributeNode.text := (self as TAtomEntry).Content.Lang;
          end;
        end else begin
          if objNode <> nil then begin
            if objNode.attributes.length = 0 then begin

              objEntryNode.removeChild(objNode);

            end;
          end;
        end;

      end;
      //

      //pubControl
      if Assigned((self as TAtomEntry).PubControl) then begin
        objControlNode := entryDoc.selectSingleNode('//atom:entry/'+Atom_PubControl_NS_Prefix+':control');
        if objControlNode = nil then begin
          objControlNode := entryDoc.createNode(NODE_ELEMENT,Atom_PubControl_NS_Prefix+':control',Atom_PubControl_NS);
          objEntryNode.appendChild(objControlNode);
        end;
        objNode := entryDoc.selectSingleNode('//atom:entry/'+Atom_PubControl_NS_Prefix+':control/'+Atom_PubControl_NS_Prefix+':draft');
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,Atom_PubControl_NS_Prefix+':draft',Atom_PubControl_NS);
          objControlNode.appendChild(objNode);
        end;
        if ((self as TAtomEntry).PubControl.PubDraft) then begin
          objNode.text := 'yes';
        end else begin
          objNode.text := 'no';
        end;
      end;


      //Rights
      objNode := entryDoc.selectSingleNode('//atom:entry/atom:rights');
      if objNode <> nil then begin

        objNode.text := (self as TAtomEntry).Rights.Text;

      end else begin
        if (self as TAtomEntry).Rights.Text <> '' then begin

          objNode := entryDoc.createNode(NODE_ELEMENT,'rights',Atom_NS);
          objEntryNode.appendChild(objNode);
          objNode.text := (self as TAtomEntry).Rights.Text;

        end;
      end;
      if objNode <> nil then begin
        //@xml:base
        if ((self as TAtomEntry).Rights.FBase <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).Rights.FBase;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
        end;
        //@xml:lang
        if ((self as TAtomEntry).Rights.Lang <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).Rights.Lang;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
        end;
      end;
      //This one below was totally fail. Fixed above. This bug causes Rights element to be dropped.
      (*
      if (self as TAtomEntry).Rights.Text <> '' then begin
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,'rights',Atom_NS);
          objEntryNode.appendChild(objNode);
        end;
        objNode.text := (self as TAtomEntry).Rights.Text;

        //@xml:base
        if ((self as TAtomEntry).Rights.FBase <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).Rights.FBase;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
        end;
        //@xml:lang
        if ((self as TAtomEntry).Rights.Lang <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if objAttributeNode = nil then begin
            objAttributeNode := entryDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomEntry).Rights.Lang;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
        end;

      end else begin
        if Assigned(objNode) then objEntryNode.removeChild(objNode);
      end;
      *)

      //Source
      objNode := entryDoc.selectSingleNode('//atom:entry/atom:source');
      if objNode <> nil then begin

        objNode.text := (self as TAtomEntry).Source;

      end else begin
        if (self as TAtomEntry).Source <> '' then begin

          objNode := entryDoc.createNode(NODE_ELEMENT,'source',Atom_NS);
          objEntryNode.appendChild(objNode);
          objNode.text := (self as TAtomEntry).Source;

        end;
      end;
      //This one below was totally fail. same reason as above <rights> element.
      (*
      if (self as TAtomEntry).Source <> '' then begin
        if objNode = nil then begin
          objNode := entryDoc.createNode(NODE_ELEMENT,'source',Atom_NS);
          objEntryNode.appendChild(objNode);
        end;
        objNode.text := (self as TAtomEntry).Source;
      end else begin
        if Assigned(objNode) then objEntryNode.removeChild(objNode);
      end;
      *)


      result:=entryDoc;
    end;
  end;

end;


function TAtomEntry.GetObjNodeFromXML(XML:WideString):IXMLDOMNode;
var
  tmpDoc :IXMLDOMDocument;
  XMLError:TXMLErrorInfo;
begin
  result := nil;
  tmpDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument;
  tmpDoc.async :=  False;
  tmpDoc.validateOnParse := false;
  tmpDoc.resolveexternals:=false;
  tmpDoc.preserveWhiteSpace := true;
  tmpDoc.loadXML(XML);
  if (tmpDoc.readyState = 4) Then begin
    if (tmpDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=tmpDoc.parseError.errorCode;
      XMLError.Reason:=tmpDoc.parseError.reason;
      XMLError.SrcText:=tmpDoc.parseError.srcText;
      XMLError.Line:=tmpDoc.parseError.line;
      XMLError.LinePos:=tmpDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      result := tmpDoc.documentElement;

    end;
  end;

end;

{ TAtomEntryList :list of TAtomEntry}
function TAtomEntryList.Get(Index: Integer): TAtomEntry;
begin
  Result :=  TAtomEntry( inherited Get(Index) );
end;

procedure TAtomEntryList.Put(Index: Integer; const Value: TAtomEntry);
begin
  inherited Put( Index, Value );
end;

destructor TAtomEntryList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;


{ TAtomBinary }
constructor TAtomBinary.Create;
begin
  //Binary := TMemoryStream.Create;
end;

destructor TAtomBinary.Destroy;
begin
  //if Assigned(Binary) then Binary.Free;
  inherited Destroy;
end;


{ TAtomFeed }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomFeed.Create(baseUri:string = '');
begin
  Title := TAtomTextConstruct.Create;
  SubTitle := TAtomTextConstruct.Create;
  Links := TAtomLinkList.Create;
  Entries := TAtomEntryList.Create;
  Authors := TAtomPersonList.Create;
  Contributors := TAtomPersonList.Create;
  Categories := TAtomCategoryList.Create;

  (Self as TAtomFeed).parentBase := baseUri;
end;

constructor TAtomFeed.Create(fromXML:WideString; baseUri:string);
var
  feedDoc:IXMLDOMDocument2;
  objFeedNode:IXMLDOMNode;
  objNode:IXMLDOMNode;
  objList:IXMLDOMNodelist;
  objAttributeNode:IXMLDOMNode;
  objLinkList:IXMLDOMNodelist;
  i,c:integer;
  x,z:integer;
  XMLError:TXMLErrorInfo;
  {$IFDEF AtomStrictMode}
  Error:TErrorInfo;
  {$ENDIF}
begin
  Title := TAtomTextConstruct.Create;
  SubTitle := TAtomTextConstruct.Create;
  Links := TAtomLinkList.Create;
  Entries := TAtomEntryList.Create;
  Authors := TAtomPersonList.Create;
  Contributors := TAtomPersonList.Create;
  Categories := TAtomCategoryList.Create;

  //keep original. this preserves foregin markups.
  (Self as TAtomFeed).FXML := fromXML;

  //build TAtomFeed From XML, sets values etc. This creates TAtomFeed from APP Feed XML Document

  feedDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  feedDoc.async :=  False;
  feedDoc.validateOnParse := false;
  feedDoc.resolveexternals:=false;
  //feedDoc.preserveWhiteSpace := true;

  feedDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  feedDoc.loadXML((Self as TAtomFeed).FXML);

  if (feedDoc.readyState = 4) Then begin
    if (feedDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=feedDoc.parseError.errorCode;
      XMLError.Reason:=feedDoc.parseError.reason;
      XMLError.SrcText:=feedDoc.parseError.srcText;
      XMLError.Line:=feedDoc.parseError.line;
      XMLError.LinePos:=feedDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objFeedNode:=feedDoc.documentElement;
      if objFeedNode = nil then exit;
      //if objFeedNode.baseName <> 'feed' then exit;
      {$IFDEF AtomStrictMode}
      if objFeedNode.baseName <> 'feed' then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := 'FeedNode.baseName != "feed".';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}
      //if objFeedNode.namespaceURI <> Atom_NS then exit;
      {$IFDEF AtomStrictMode}
      if objFeedNode.namespaceURI <> Atom_NS then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := 'FeedNode.namespaceURI != Atom_NS.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

      (Self as TAtomFeed).parentBase := baseUri;

      //xml:base and xml:lang
      objAttributeNode := objFeedNode.attributes.getNamedItem('xml:base');
      if objAttributeNode <> nil then begin
        (Self as TAtomFeed).Base := objAttributeNode.nodeValue;
      end;
      objAttributeNode := objFeedNode.attributes.getNamedItem('xml:lang');
      if objAttributeNode <> nil then begin
        (Self as TAtomFeed).Lang := objAttributeNode.nodeValue;
      end;

      objNode := feedDoc.selectSingleNode('//atom:feed/atom:id');
      if objNode <> nil then begin
        (Self as TAtomFeed).Id := objNode.text;
      end;
      {$IFDEF AtomStrictMode}
      if (Self as TAtomFeed).Id = '' then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;
        Error.Description := '//atom:feed/atom:id is empty.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

      //TODO: same as TAtomEntry's TAtomLinkList.. so remove this
      objLinkList := feedDoc.selectNodes('//atom:feed/atom:link');
      if objLinkList <> nil then begin
        for c:=0 to objLinkList.length-1 do begin

          z := (Self as TAtomFeed).Links.Add(TAtomLink.Create);

          //xml:base and xml:lang
          objAttributeNode := objLinkList.item[c].attributes.getNamedItem('xml:base');
          if objAttributeNode <> nil then begin
            (Self as TAtomFeed).Links[z].Base := objAttributeNode.nodeValue;
          end;
          objAttributeNode := objLinkList.item[c].attributes.getNamedItem('xml:lang');
          if objAttributeNode <> nil then begin
            (Self as TAtomFeed).Links[z].Lang := objAttributeNode.nodeValue;
          end;
          (Self as TAtomFeed).Links[z].parentBase := (Self as TAtomFeed).Base;

          if (objLinkList.item[c].attributes.getNamedItem('rel') <> nil) then begin
            (Self as TAtomFeed).Links[z].Rel := objLinkList.item[c].attributes.getNamedItem('rel').text;
          end;
          if (objLinkList.item[c].attributes.getNamedItem('type') <> nil) then begin
            (Self as TAtomFeed).Links[z].ContentType := objLinkList.item[c].attributes.getNamedItem('type').text;
          end;
          if (objLinkList.item[c].attributes.getNamedItem('href') <> nil) then begin
            (Self as TAtomFeed).Links[z].Href.parentBase := (Self as TAtomFeed).Links[z].Base;
            (Self as TAtomFeed).Links[z].Href.Uri := objLinkList.item[c].attributes.getNamedItem('href').text;
          end;
          if (objLinkList.item[c].attributes.getNamedItem('title') <> nil) then begin
            (Self as TAtomFeed).Links[z].Title := objLinkList.item[c].attributes.getNamedItem('title').text;
          end;
          if (objLinkList.item[c].attributes.getNamedItem('length') <> nil) then begin
            (Self as TAtomFeed).Links[z].Length := objLinkList.item[c].attributes.getNamedItem('length').text;
          end;
          if (objLinkList.item[c].attributes.getNamedItem('hreflang') <> nil) then begin
            (Self as TAtomFeed).Links[z].Hreflang := objLinkList.item[c].attributes.getNamedItem('hreflang').text;
          end;
          (Self as TAtomFeed).Links[z].FXML:=objLinkList.item[c].xml;

        end;
      end;

      objNode := feedDoc.selectSingleNode('//atom:feed/atom:title');
      if objNode <> nil then begin
        (Self as TAtomFeed).Title.Text := objNode.text;
        if (objNode.attributes.getNamedItem('type') <> nil) then begin
          if ((objNode as IXMLDOMElement).attributes.getNamedItem('type').text = 'text') then begin
            (Self as TAtomFeed).Title.TextType := ttText;
          end else if ((objNode as IXMLDOMElement).attributes.getNamedItem('type').text = 'html') then begin
            (Self as TAtomFeed).Title.TextType := ttHtml;
          end else if ((objNode as IXMLDOMElement).attributes.getNamedItem('type').text = 'xhtml') then begin
            (Self as TAtomFeed).Title.TextType := ttXhtml;
          end;
        end;
        //TODO: xml:base, xml:lang
      end;

      objNode := feedDoc.selectSingleNode('//atom:feed/atom:subtitle');
      if objNode <> nil then begin
        (Self as TAtomFeed).SubTitle.Text := objNode.text;
        if objNode.attributes.getNamedItem('type') <> nil then begin
          if (objNode.attributes.getNamedItem('type').text = 'text') then begin
            (Self as TAtomFeed).Title.TextType := ttText;
          end else if (objNode.attributes.getNamedItem('type').text = 'html') then begin
            (Self as TAtomFeed).Title.TextType := ttHtml;
          end else if (objNode.attributes.getNamedItem('type').text = 'xhtml') then begin
            (Self as TAtomFeed).SubTitle.Text := objNode.xml;
            (Self as TAtomFeed).Title.TextType := ttXhtml;
          end;
        end;
        //TODO: xml:base, xml:lang
      end;

      objList := feedDoc.selectNodes('//atom:feed/atom:author');
      if objList <> nil then begin
        for i:=0 to objList.length -1 do begin
          x := (Self as TAtomFeed).Authors.Add(TAtomPerson.Create);
          (Self as TAtomFeed).Authors.Items[x].parentBase:= (Self as TAtomFeed).Base;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:name');
          if objNode <> nil then begin
            (Self as TAtomFeed).Authors.Items[x].Name := objNode.text;
          end;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:uri');
          if objNode <> nil then begin
            (Self as TAtomFeed).Authors.Items[x].Uri.parentBase := (Self as TAtomFeed).Authors.Items[x].Base;
            (Self as TAtomFeed).Authors.Items[x].Uri.Uri := objNode.text;
          end;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:email');
          if objNode <> nil then begin
            (Self as TAtomFeed).Authors.Items[x].Email := objNode.text;
          end;

          (Self as TAtomFeed).Authors.Items[x].FXML:=objList.item[i].xml;
          //TODO: xml:base, xml:lang
        end;
      end;

      objList := feedDoc.selectNodes('//atom:feed/atom:contributor');
      if objList <> nil then begin
        for i:=0 to objList.length -1 do begin
          x := (Self as TAtomFeed).Contributors.Add(TAtomPerson.Create);
          (Self as TAtomFeed).Contributors.Items[x].parentBase:= (Self as TAtomFeed).Base;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:name');
          if objNode <> nil then begin
            (Self as TAtomFeed).Contributors.Items[x].Name := objNode.text;
          end;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:uri');
          if objNode <> nil then begin
            (Self as TAtomFeed).Contributors.Items[x].Uri.parentBase := (Self as TAtomFeed).Contributors.Items[x].Base;
            (Self as TAtomFeed).Contributors.Items[x].Uri.Uri := objNode.text;
          end;
          objNode := (objList.item[i] as IXMLDOMElement).selectSingleNode('atom:email');
          if objNode <> nil then begin
            (Self as TAtomFeed).Contributors.Items[x].Email := objNode.text;
          end;
          
          (Self as TAtomFeed).Contributors.Items[x].FXML:=objList.item[i].xml;
          //TODO: xml:base, xml:lang
        end;
      end;

      objList := feedDoc.selectNodes('//atom:feed/atom:entry');
      if objList <> nil then begin
        for i:=0 to objList.length -1 do begin

          (Self as TAtomFeed).Entries.Add(TAtomEntry.Create((objList.item[i] as IXMLDOMElement).xml,(Self as TAtomFeed).Base));
              
        end;
      end;
      //
      objNode := feedDoc.selectSingleNode('//atom:feed/atom:updated');
      if objNode <> nil then begin
        if objNode.text <> '' then begin
          (Self as TAtomFeed).DateTimeUpdated := XMLTimeToDateTime(objNode.text,false);
        end;
      end;
      {$IFDEF AtomStrictMode}
      if YearOf((Self as TAtomFeed).DateTimeUpdated) = 1899 then begin
        Error:=TErrorInfo.create;
        Error.Title := MsgStrAtomInvalid;//'Atom Format Error.';
        Error.Description := '//atom:feed/atom:updated is empty.';
        raise EAtomError.Create(Error);
        Error.Free;
        exit;
      end;
      {$ENDIF}

    end;
  end;

end;

destructor TAtomFeed.Destroy;
begin
  Title.Free;
  SubTitle.Free;
  Links.Free;
  Icon.Free;
  Logo.Free;
  Entries.Free;
  Authors.Free;
  Contributors.Free;
  Categories.Free;
  Rights.Free;
  inherited Destroy;
end;

function TAtomFeed.getFirstUri:string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomFeed(self).Links.Count-1 do begin
    if UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'FIRST' then begin
      //if TAtomFeed(self).Links.Items[i].LinkType = 'application/atom+xml' then begin
        //result := TAtomFeed(self).Links.Items[i].Href;
        result := TAtomFeed(self).Links.Items[i].Href.Uri;
      //end;
    end;
  end;
end;

function TAtomFeed.getPrevUri:string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomFeed(self).Links.Count-1 do begin
    if (UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'PREV') or
      (UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'PREVIOUS') then begin
      //if TAtomFeed(self).Links.Items[i].LinkType = 'application/atom+xml' then begin
        //result := TAtomFeed(self).Links.Items[i].Href;
        result := TAtomFeed(self).Links.Items[i].Href.Uri;
      //end;
    end;
  end;
end;

function TAtomFeed.getNextUri:string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomFeed(self).Links.Count-1 do begin
    if UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'NEXT' then begin
      //if TAtomFeed(self).Links.Items[i].LinkType = 'application/atom+xml' then begin
        //result := TAtomFeed(self).Links.Items[i].Href;
        result := TAtomFeed(self).Links.Items[i].Href.Uri;
      //end;
    end;
  end;
end;

function TAtomFeed.getLastUri:string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomFeed(self).Links.Count-1 do begin
    if UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'LAST' then begin
      //if TAtomFeed(self).Links.Items[i].LinkType = 'application/atom+xml' then begin
        //result := TAtomFeed(self).Links.Items[i].Href;
        result := TAtomFeed(self).Links.Items[i].Href.Uri;
      //end;
    end;
  end;
end;

function TAtomFeed.getSelfUri:string;
var
  i:integer;
begin
  result:='';
  for i:=0 to TAtomFeed(self).Links.Count-1 do begin
    if UpperCase(TAtomFeed(self).Links.Items[i].Rel) = 'SELF' then begin
      //if TAtomFeed(self).Links.Items[i].LinkType = 'application/atom+xml' then begin
        //result := TAtomFeed(self).Links.Items[i].Href;
        result := TAtomFeed(self).Links.Items[i].Href.Uri;
      //end;
    end;
  end;
end;

function TAtomFeed.asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objAttributeNode, objNodeChild, objNode: IXMLDOMNode;
  i,c:integer;
  objNodeList:IXMLDOMNodeList;
begin
  result:=nil;
  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'feed',Atom_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'feed' then exit;
      if objDocumentNode.namespaceURI <> Atom_NS then exit;

      //@xml:base          use Base property here.
      if (FBase <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := FBase;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      //ID
      if ((self as TAtomFeed).Id = '') then begin
        //id is required
        (self as TAtomFeed).Id := 'urn:uuid:'+CreateClassID; //create uuid or guid
      end;
      objNode := Doc.documentElement.selectSingleNode('atom:id');
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'id',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomFeed).Id;

      //TITLE
      objNode := Doc.documentElement.selectSingleNode('atom:title');
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'title',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomFeed).Title.Text;
      //@TYPE
      objAttributeNode := objNode.attributes.getNamedItem('type');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
        objNode.attributes.setNamedItem(objAttributeNode);
      end;
      case (self as TAtomFeed).Title.TextType of
        ttText:objAttributeNode.text := 'text';
        ttHtml:objAttributeNode.text := 'html';
        ttXhtml:objAttributeNode.text := 'xhtml';
      end;
      //@xml:base
      if ((self as TAtomFeed).Title.FBase <> '') then begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomFeed).Title.FBase;
      end else begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if ((self as TAtomFeed).Title.Lang <> '') then begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomFeed).Title.Lang;
      end else begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
      end;

      //SUBTITLE
      objNode := Doc.documentElement.selectSingleNode('atom:subtitle');
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'subtitle',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomFeed).SubTitle.Text;
      //@TYPE
      objAttributeNode := objNode.attributes.getNamedItem('type');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
        objNode.attributes.setNamedItem(objAttributeNode);
      end;
      case (self as TAtomFeed).SubTitle.TextType of
        ttText:objAttributeNode.text := 'text';
        ttHtml:objAttributeNode.text := 'html';
        ttXhtml:objAttributeNode.text := 'xhtml';
      end;
      //@xml:base
      if ((self as TAtomFeed).SubTitle.FBase <> '') then begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomFeed).SubTitle.FBase;
      end else begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if ((self as TAtomFeed).SubTitle.Lang <> '') then begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomFeed).SubTitle.Lang;
      end else begin
        objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
      end;

      //authors
      //delete authors
      objNodeList:= Doc.documentElement.selectNodes('//atom:feed/atom:author');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          Doc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomFeed).Authors.Count > 0) then begin
        for i:=0 to (self as TAtomFeed).Authors.Count-1 do begin
          if Assigned((self as TAtomFeed).Authors[i]) then begin
             Doc.documentElement.appendChild((self as TAtomFeed).Authors[i].asXMLObj('author').documentElement);

          end;
        end;
      end;

      //contributors
      //delete contributors
      objNodeList:= Doc.documentElement.selectNodes('//atom:feed/atom:contributor');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          Doc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomFeed).Contributors.Count > 0) then begin
        for i:=0 to (self as TAtomFeed).Contributors.Count-1 do begin
          if Assigned((self as TAtomFeed).Contributors[i]) then begin
            Doc.documentElement.appendChild((self as TAtomFeed).Contributors[i].asXMLObj('contributor').documentElement);

          end;
        end;
      end;

      //LINKS
      //delete link
      objNodeList:= Doc.documentElement.selectNodes('//atom:feed/atom:link');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          Doc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomFeed).Links.Count > 0) then begin
        for i:=0 to (self as TAtomFeed).Links.Count-1 do begin
          if Assigned((self as TAtomFeed).Links[i]) then begin
            Doc.documentElement.appendChild((self as TAtomFeed).Links[i].asXMLObj.documentElement);
          end;
        end;
      end;

      //categories
      //delete categories
      objNodeList:= Doc.documentElement.selectNodes('//atom:feed/atom:category');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          Doc.documentElement.removeChild(objNodeList.item[c]);
        end;
      end;

      if ((self as TAtomFeed).Categories.Count > 0) then begin
        for i:=0 to (self as TAtomFeed).Categories.Count-1 do begin
          if Assigned((self as TAtomFeed).Categories[i]) then begin
            Doc.documentElement.appendChild((self as TAtomFeed).Categories[i].asXMLObj.documentElement);

          end;
        end;
      end;

      //ICON
      if Assigned((self as TAtomFeed).Icon) then begin
        if (self as TAtomFeed).Icon.Uri <> '' then begin
          objNode := Doc.documentElement.selectSingleNode('atom:icon');
          if objNode = nil then begin
            objNode := Doc.createNode(NODE_ELEMENT,'icon',Atom_NS);
            objDocumentNode.appendChild(objNode);
          end;
          objNode.text := (self as TAtomFeed).Icon.Uri;
        end else begin
          objNode := Doc.documentElement.selectSingleNode('atom:icon');
          if objNode = nil then begin
            Doc.documentElement.removeChild(objNode);
          end;
        end;
      end else begin
        objNode := Doc.documentElement.selectSingleNode('atom:icon');
        if Assigned(objNode) then begin
          Doc.documentElement.removeChild(objNode);
        end;
      end;

      //Logo
      if Assigned((self as TAtomFeed).Logo) then begin
        if (self as TAtomFeed).Logo.Uri <> '' then begin
          objNode := Doc.documentElement.selectSingleNode('atom:logo');
          if objNode = nil then begin
            objNode := Doc.createNode(NODE_ELEMENT,'logo',Atom_NS);
            objDocumentNode.appendChild(objNode);
          end;
          objNode.text := (self as TAtomFeed).Logo.Uri;
        end else begin
          objNode := Doc.documentElement.selectSingleNode('atom:logo');
          if objNode = nil then begin
            Doc.documentElement.removeChild(objNode);
          end;
        end;
      end else begin
        objNode := Doc.documentElement.selectSingleNode('atom:logo');
        if Assigned(objNode) then begin
          Doc.documentElement.removeChild(objNode);
        end;
      end;

      //Generator
      if (self as TAtomFeed).Generator <> '' then begin
        objNode := Doc.documentElement.selectSingleNode('atom:generator');
        if objNode = nil then begin
          objNode := Doc.createNode(NODE_ELEMENT,'generator',Atom_NS);
          objDocumentNode.appendChild(objNode);
        end;
        objNode.text := (self as TAtomFeed).Generator;
      end else begin
        objNode := Doc.documentElement.selectSingleNode('atom:generator');
        if Assigned(objNode) then begin
          Doc.documentElement.removeChild(objNode);
        end;
      end;

      //Rights
      if Assigned((self as TAtomFeed).Rights) then begin
        objNode := Doc.documentElement.selectSingleNode('atom:rights');
        if objNode = nil then begin
          objNode := Doc.createNode(NODE_ELEMENT,'rights',Atom_NS);
          objDocumentNode.appendChild(objNode);
        end;
        objNode.text := (self as TAtomFeed).Rights.Text;
        //@TYPE
        objAttributeNode := objNode.attributes.getNamedItem('type');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'type',Atom_NS);
          objNode.attributes.setNamedItem(objAttributeNode);
        end;
        case (self as TAtomFeed).Rights.TextType of
          ttText:objAttributeNode.text := 'text';
          ttHtml:objAttributeNode.text := 'html';
          ttXhtml:objAttributeNode.text := 'xhtml';
        end;
        //@xml:base
        if ((self as TAtomFeed).Rights.FBase <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if objAttributeNode = nil then begin
            objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomFeed).Rights.FBase;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:base');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:base');
        end;
        //@xml:lang
        if ((self as TAtomFeed).Rights.Lang <> '') then begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if objAttributeNode = nil then begin
            objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
            objNode.attributes.setNamedItem(objAttributeNode);
          end;
          objAttributeNode.text := (self as TAtomFeed).Rights.Lang;
        end else begin
          objAttributeNode := objNode.attributes.getNamedItem('xml:lang');
          if Assigned(objAttributeNode) then objNode.attributes.removeNamedItem('xml:lang');
        end;

      end else begin
        objNode := Doc.documentElement.selectSingleNode('atom:rights');
        if Assigned(objNode) then begin
          Doc.documentElement.removeChild(objNode);
        end;
      end;




      //updated
      if YearOf((self as TAtomFeed).DateTimeUpdated) > 1899 then begin
        objNode := Doc.documentElement.selectSingleNode('//atom:feed/atom:updated');
        if objNode = nil then begin
          objNode := Doc.createNode(NODE_ELEMENT,'updated',Atom_NS);
          Doc.documentElement.appendChild(objNode);
        end;
        objNode.text := DateTimeToXMLTime((self as TAtomFeed).DateTimeUpdated,true);
      end;


      //delete entry
      objNodeList:= Doc.documentElement.selectNodes('atom:entry');
      if objNodeList <> nil then begin
        if objNodeList.length > 0 then begin
          for c:=0 to objNodeList.length-1 do begin
            objNodeList.item[c].parentNode.removeChild(objNodeList.item[c]);
          end;
        end;
      end;

      if recurse then begin
        if ((self as TAtomFeed).Entries.Count > 0) then begin
          for i:=0 to (self as TAtomFeed).Entries.Count-1 do begin
            if Assigned((self as TAtomFeed).Entries[i]) then begin

              objNodeChild := (self as TAtomFeed).Entries[i].asXMLObj.documentElement;
              if Assigned(objNodeChild) then begin
                objDocumentNode.appendChild(objNodeChild);
              end;

            end;
          end;
        end;

      end;
      result:=Doc;
    end;
  end;

end;

function TAtomFeed.mergeEntriesFromFeed(feed:TAtomFeed):TAtomFeed;
var
  i,c:integer;
  blnFound:boolean;
  ChangedFeed:TAtomFeed;
begin
  ChangedFeed:=TAtomFeed.Create();

  for i := 0 to feed.Entries.Count - 1 do begin
    blnFound:=false;
    for c := 0 to (self as TAtomFeed).Entries.Count - 1 do begin

      if feed.Entries[i].Id = (self as TAtomFeed).Entries[c].Id then begin
        blnFound:=true;

        if (YearOf(feed.Entries[i].DateTimeEdited) > 1899) and (YearOf((self as TAtomFeed).Entries[c].DateTimeEdited) > 1899) then begin
          if feed.Entries[i].DateTimeEdited > (self as TAtomFeed).Entries[c].DateTimeEdited then begin

            (self as TAtomFeed).Entries[c].Free;
            (self as TAtomFeed).Entries[c] := TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,'');

            ChangedFeed.Entries.Add(TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,''));

          end;
        end else if (YearOf(feed.Entries[i].DateTimeUpdated) > 1899) and (YearOf((self as TAtomFeed).Entries[c].DateTimeUpdated) > 1899) then begin
          if feed.Entries[i].DateTimeUpdated > (self as TAtomFeed).Entries[c].DateTimeUpdated then begin

            (self as TAtomFeed).Entries[c].Free;
            (self as TAtomFeed).Entries[c] := TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,'');

            ChangedFeed.Entries.Add(TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,''));

          end;
        end;


      end;

    end;
    if not blnFound then begin
      //it's new
      if (YearOf(feed.Entries[i].DateTimeUpdated) > 1899) then begin

        //(self as TAtomFeed).Entries.Add(TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,''));
        (self as TAtomFeed).Entries.Insert(0,TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,'')); //.Add(TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,''));

        ChangedFeed.Entries.Add(TAtomEntry.Create(feed.Entries[i].asXMLObj.xml,''));

      end;
    end;
  end;
  result:=ChangedFeed;
end;


{ TAtomCollection }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomCollection.Create;
begin
  href := TAtomUri.Create(self.Base);
  AcceptMediaRangeList:= TStringList.Create;

  Categories:=TAtomCategories.Create;

end;

constructor TAtomCollection.Create(fromXML:WideString; baseUri:string;recurse:boolean=true);
var
  CollectionDoc:IXMLDOMDocument2;
  objAcceptMediaRangeList:IXMLDOMNodelist;
  objCategoryList:IXMLDOMNodelist;
  objCollectionNode:IXMLDOMNode;
  XMLError:TXMLErrorInfo;
  m:integer;
begin
  href := TAtomUri.Create(self.Base);
  AcceptMediaRangeList:= TStringList.Create;

  Categories:=TAtomCategories.Create;

  (Self as TAtomCollection).parentBase:=baseUri;

  (Self as TAtomCollection).FXML := fromXML;
  //build TAtomFeed From XML, sets values etc. This creates TAtomFeed from APP Feed XML Document

  CollectionDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  CollectionDoc.async :=  False;
  CollectionDoc.validateOnParse := false;
  CollectionDoc.resolveexternals:=false;
  //ServiceDoc.preserveWhiteSpace := true;

  CollectionDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  CollectionDoc.loadXML((Self as TAtomCollection).FXML);

  if (CollectionDoc.readyState = 4) Then begin
    if (CollectionDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=CollectionDoc.parseError.errorCode;
      XMLError.Reason:=CollectionDoc.parseError.reason;
      XMLError.SrcText:=CollectionDoc.parseError.srcText;
      XMLError.Line:=CollectionDoc.parseError.line;
      XMLError.LinePos:=CollectionDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objCollectionNode:=CollectionDoc.documentElement;
      if objCollectionNode = nil then exit;
      if objCollectionNode.baseName <> 'collection' then exit;
      if objCollectionNode.namespaceURI <> Atom_APP_NS then exit;

      //(Self as TAtomCollection).Base := baseUri;

      if (CollectionDoc.documentElement.selectSingleNode('atom:title') <> nil) then begin
        (Self as TAtomCollection).Title := CollectionDoc.documentElement.selectSingleNode('atom:title').text;
      end;

      if (CollectionDoc.documentElement.attributes.getNamedItem('xml:lang') <> nil) then begin
        (Self as TAtomCollection).Lang := CollectionDoc.documentElement.attributes.getNamedItem('xml:lang').nodeValue;
      end;

      if (CollectionDoc.documentElement.attributes.getNamedItem('xml:base') <> nil) then begin
        (Self as TAtomCollection).Base := CollectionDoc.documentElement.attributes.getNamedItem('xml:base').nodeValue;
      end;
      //(Self as TAtomCollection).parentBase := (Self as TAtomService).Workspaces[x].Base;

      if (CollectionDoc.documentElement.attributes.getNamedItem('href') <> nil) then begin
        (Self as TAtomCollection).Href.parentBase := (Self as TAtomCollection).Base;
        (Self as TAtomCollection).Href.Uri := CollectionDoc.documentElement.attributes.getNamedItem('href').nodeValue;
      end;

      objAcceptMediaRangeList := CollectionDoc.documentElement.selectNodes('app:accept');
      if (objAcceptMediaRangeList <> nil) then begin
        if objAcceptMediaRangeList.length > 0 then begin
          if objAcceptMediaRangeList.length = 1 then begin
            if trim(objAcceptMediaRangeList.item[0].text) = '' then begin
              //If one accept element exists and is empty, clients SHOULD assume that the Collection does not support the creation of new Entries.
            end else begin
              (Self as TAtomCollection).AcceptMediaRangeList.Add(trim(objAcceptMediaRangeList.item[0].text));
            end;
          end else begin
            for m:=0 to objAcceptMediaRangeList.length -1 do begin
              (Self as TAtomCollection).AcceptMediaRangeList.Add(trim(objAcceptMediaRangeList.item[m].text));
            end;
          end;

        end else begin
          //none. assume it default entry
          (Self as TAtomCollection).AcceptMediaRangeList.Add('application/atom+xml;type=entry');
        end;
      end else begin
        //not gonna happen?
        (Self as TAtomCollection).AcceptMediaRangeList.Add('application/atom+xml;type=entry');
      end;

      if recurse then begin

        if (CollectionDoc.documentElement.selectSingleNode('app:categories') <> nil) then begin
            //TODO: get xml:base and lang

          if CollectionDoc.documentElement.selectSingleNode('app:categories').attributes.getNamedItem('href') <> nil then begin
            // outOfLineCategories (get them lator)
            //(Self as TAtomService).Workspaces[x].Collections[z].CategoryDocumentRef := TAtomCategoryDocumentRef.Create((Self as TAtomService).Workspaces[x].Collections[z].Base);

            (Self as TAtomCollection).Categories.parentBase := (Self as TAtomCollection).Base;
            if CollectionDoc.documentElement.selectSingleNode('app:categories').attributes.getNamedItem('href') <> nil then begin
              (Self as TAtomCollection).Categories.Href.Uri := CollectionDoc.documentElement.selectSingleNode('app:categories').attributes.getNamedItem('href').text;
            end;
          end else begin
            // InlineCategories

            objCategoryList := CollectionDoc.documentElement.selectNodes('atom:category');
            (Self as TAtomCollection).Categories.setCategories(CollectionDoc.documentElement.selectSingleNode('app:categories').xml,(Self as TAtomCollection).Base);

          end;

        end;

      end;
      //(Self as TAtomCollection).FXML:=CollectionDoc.xml;
    end;
  end;

end;

function TAtomCollection.asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objAttributeNode, objNodeChild, objNode: IXMLDOMNode;
  i,c:integer;
  objNodeList:IXMLDOMNodeList;
begin
  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  result:=Doc;

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'collection',Atom_APP_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'collection' then exit;
      if objDocumentNode.namespaceURI <> Atom_APP_NS then exit;

      //@xml:base          use Base property here.
      if (Base <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Base;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_APP_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      objAttributeNode := Doc.documentElement.attributes.getNamedItem('href');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'href',Atom_NS);
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;
      objAttributeNode.text := (self as TAtomCollection).Href.Uri;

      
      objNode := objDocumentNode.selectSingleNode('atom:title');
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'title',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomCollection).Title;

      objNode:= nil;

      objNodeList := objDocumentNode.selectNodes('app:accept');
      if objNodeList.length > 0 then begin
        for c:=0 to objNodeList.length-1 do begin
          objDocumentNode.removeChild(objNodeList.item[c]);
        end;
      end;
      if (self as TAtomCollection).AcceptMediaRangeList.Count > 0 then begin
        for i := 0 to (self as TAtomCollection).AcceptMediaRangeList.Count - 1 do begin

          if objNode = nil then begin
            objNode := Doc.createNode(NODE_ELEMENT,'accept',Atom_APP_NS);
            objDocumentNode.appendChild(objNode);
          end;
          objNode.text := (self as TAtomCollection).AcceptMediaRangeList.Strings[i];
          objNode:= nil;
        end;
      end else begin
        objNode := Doc.createNode(NODE_ELEMENT,'accept',Atom_APP_NS);
        objNode.text := ''; //This means nothing is allowed to post.
        objDocumentNode.appendChild(objNode);
      end;

      objNode := nil;
      objNode := objDocumentNode.selectSingleNode('app:categories');
      if objNode <> nil then begin
        objDocumentNode.removeChild(objNode);
      end;


      if recurse then begin
        if Assigned((self as TAtomCollection).Categories) then begin
          objNodeChild := (self as TAtomCollection).Categories.asXMLObj.documentElement;
          if Assigned(objNodeChild) then begin
            objDocumentNode.appendChild(objNodeChild);
          end;
        end;
      end;

    end;
  end;

end;

destructor TAtomCollection.Destroy;
begin
  AcceptMediaRangeList.Free;
  href.Free;
  if Assigned(Categories) then Categories.Free;
  inherited Destroy;
end;

{ TAtomCollectionList : list of TAtomCollection}
function TAtomCollectionList.Get(Index: Integer): TAtomCollection;
begin
  Result :=  TAtomCollection( inherited Get(Index) );
end;

procedure TAtomCollectionList.Put(Index: Integer; const Value: TAtomCollection);
begin
  inherited Put( Index, Value );
end;

destructor TAtomCollectionList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;

{ TAtomWorkspace }
////////////////////////////////////////////////////////////////////////////////

constructor TAtomWorkspace.Create;
begin
  Collections := TAtomCollectionList.Create;
end;

constructor TAtomWorkspace.Create(fromXML:WideString; baseUri:string; recurse:boolean=true);
var
  WorkspaceDoc:IXMLDOMDocument2;
  objCollectionList:IXMLDOMNodelist;
  objWorkspaceNode:IXMLDOMNode;
  XMLError:TXMLErrorInfo;
  c:integer;
begin

  Collections := TAtomCollectionList.Create;

  (Self as TAtomWorkspace).parentBase:=baseUri;

  (Self as TAtomWorkspace).FXML := fromXML;
  //build TAtomFeed From XML, sets values etc. This creates TAtomFeed from APP Feed XML Document

  WorkspaceDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  WorkspaceDoc.async :=  False;
  WorkspaceDoc.validateOnParse := false;
  WorkspaceDoc.resolveexternals:=false;
  //ServiceDoc.preserveWhiteSpace := true;

  WorkspaceDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  WorkspaceDoc.loadXML((Self as TAtomWorkspace).FXML);

  if (WorkspaceDoc.readyState = 4) Then begin
    if (WorkspaceDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=WorkspaceDoc.parseError.errorCode;
      XMLError.Reason:=WorkspaceDoc.parseError.reason;
      XMLError.SrcText:=WorkspaceDoc.parseError.srcText;
      XMLError.Line:=WorkspaceDoc.parseError.line;
      XMLError.LinePos:=WorkspaceDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objWorkspaceNode:=WorkspaceDoc.documentElement;
      if objWorkspaceNode = nil then exit;
      if objWorkspaceNode.baseName <> 'workspace' then exit;
      if objWorkspaceNode.namespaceURI <> Atom_APP_NS then exit;

      (Self as TAtomWorkspace).parentBase := baseUri;

      WorkspaceDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');
      if objWorkspaceNode.selectSingleNode('atom:title') <> nil then begin
        (Self as TAtomWorkspace).Title := objWorkspaceNode.selectSingleNode('atom:title').text;
      end;

      if (objWorkspaceNode.attributes.getNamedItem('xml:lang') <> nil) then begin
        (Self as TAtomWorkspace).Lang := objWorkspaceNode.attributes.getNamedItem('xml:lang').nodeValue;
      end;
      if (objWorkspaceNode.attributes.getNamedItem('xml:base') <> nil) then begin
        (Self as TAtomWorkspace).Base := objWorkspaceNode.attributes.getNamedItem('xml:base').nodeValue;
      end;
      //(Self as TAtomWorkspace).parentBase := baseUri;

      if recurse then begin
        WorkspaceDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');
        objCollectionList := objWorkspaceNode.selectNodes('app:collection');
        if objCollectionList <> nil then begin
          for c:=0 to objCollectionList.length -1 do begin
            (Self as TAtomWorkspace).Collections.Add(TAtomCollection.Create(objCollectionList.item[c].xml,(Self as TAtomWorkspace).Base,recurse));

          end;

        end;
        //(Self as TAtomService).Workspaces[x].FXML:=objWorkspaceList.item[w].xml;

      end;

    end;
  end;
end;

function TAtomWorkspace.asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
var
  Doc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objDocumentNode, objAttributeNode, objNodeChild, objNode: IXMLDOMNode;
  i,c:integer;
  objNodeList:IXMLDOMNodeList;
begin
  result:=nil;
  Doc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  Doc.async :=  False;
  Doc.validateOnParse := false;
  Doc.resolveexternals:=false;
  //Doc.preserveWhiteSpace := true;

  Doc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    Doc.loadXML(FXML);
  end else begin
    objDocumentNode := Doc.createNode(NODE_ELEMENT,'workspace',Atom_APP_NS);
    Doc.appendChild(objDocumentNode);
  end;

  if (Doc.readyState = 4) Then begin
    if (Doc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=Doc.parseError.errorCode;
      XMLError.Reason:=Doc.parseError.reason;
      XMLError.SrcText:=Doc.parseError.srcText;
      XMLError.Line:=Doc.parseError.line;
      XMLError.LinePos:=Doc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objDocumentNode := Doc.documentElement;
      if objDocumentNode.baseName <> 'workspace' then exit;
      if objDocumentNode.namespaceURI <> Atom_APP_NS then exit;

      //@xml:base          use Base property here.
      if (Base <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Base;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if (Lang <> '') then begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objDocumentNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := Lang;
      end else begin
        objAttributeNode := Doc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objDocumentNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := Doc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := Doc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_APP_NS;
        objDocumentNode.attributes.setNamedItem(objAttributeNode);
      end;

      //
      objNode := Doc.documentElement.selectSingleNode('atom:title');
      if objNode = nil then begin
        objNode := Doc.createNode(NODE_ELEMENT,'title',Atom_NS);
        objDocumentNode.appendChild(objNode);
      end;
      objNode.text := (self as TAtomWorkspace).Title;

      //delete collection
      objNodeList:= Doc.selectNodes('//app:workspace/app:collection');
      if objNodeList <> nil then begin
        if objNodeList.length > 0 then begin
          for c:=0 to objNodeList.length-1 do begin
            objNodeList.item[c].parentNode.removeChild(objNodeList.item[c]);
          end;
        end;
      end;

      if recurse then begin
        if ((self as TAtomWorkspace).Collections.Count > 0) then begin
          for i:=0 to (self as TAtomWorkspace).Collections.Count-1 do begin
            if Assigned((self as TAtomWorkspace).Collections[i]) then begin

              objNodeChild := (self as TAtomWorkspace).Collections[i].asXMLObj.documentElement;
              if Assigned(objNodeChild) then begin
                objDocumentNode.appendChild(objNodeChild);
              end;

            end;
          end;
        end;

      end;
      result:=Doc;
    end;
  end;

end;

destructor TAtomWorkspace.Destroy;
begin
  Collections.Free;
  inherited Destroy;
end;

{ TAtomWorkspaceList : list of TAtomWorkspace}
function TAtomWorkspaceList.Get(Index: Integer): TAtomWorkspace;
begin
  Result :=  TAtomWorkspace( inherited Get(Index) );
end;

procedure TAtomWorkspaceList.Put(Index: Integer; const Value: TAtomWorkspace);
begin
  inherited Put( Index, Value );
end;

destructor TAtomWorkspaceList.Destroy;
var
  i: Integer;
begin
  for i := 0 to Self.Count - 1 do
    Self.Items[i].Free;
  inherited Destroy;
end;

{ TAtomService }
////////////////////////////////////////////////////////////////////////////////
constructor TAtomService.Create();
begin
  Workspaces := TAtomWorkspaceList.Create;

  (Self as TAtomService).parentBase := '';
end;

destructor TAtomService.Destroy;
begin
  Workspaces.Free;
  inherited Destroy;
end;

constructor TAtomService.Create(fromXML:WideString; baseUri:string;recurse:boolean=true);
var
  ServiceDoc:IXMLDOMDocument2;
  objWorkspaceList:IXMLDOMNodelist;
  objServiceNode:IXMLDOMNode;
  XMLError:TXMLErrorInfo;
  w:integer;
begin
  Workspaces := TAtomWorkspaceList.Create;

  (Self as TAtomService).parentBase:=baseUri;

  (Self as TAtomService).FXML := fromXML;
  //build TAtomFeed From XML, sets values etc. This creates TAtomFeed from APP Feed XML Document

  ServiceDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  ServiceDoc.async :=  False;
  ServiceDoc.validateOnParse := false;
  ServiceDoc.resolveexternals:=false;
  //ServiceDoc.preserveWhiteSpace := true;

  ServiceDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  ServiceDoc.loadXML((Self as TAtomService).FXML);

  if (ServiceDoc.readyState = 4) Then begin
    if (ServiceDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=ServiceDoc.parseError.errorCode;
      XMLError.Reason:=ServiceDoc.parseError.reason;
      XMLError.SrcText:=ServiceDoc.parseError.srcText;
      XMLError.Line:=ServiceDoc.parseError.line;
      XMLError.LinePos:=ServiceDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin

      objServiceNode:=ServiceDoc.documentElement;
      if objServiceNode = nil then exit;
      if objServiceNode.baseName <> 'service' then exit;
      if objServiceNode.namespaceURI <> Atom_APP_NS then exit;

      (Self as TAtomService).Base := baseUri;

      if recurse then begin

        objWorkspaceList:=ServiceDoc.selectNodes('//app:service/app:workspace');
        if objWorkspaceList <> nil then begin
          for w:=0 to objWorkspaceList.length -1 do begin
            (Self as TAtomService).Workspaces.Add(TAtomWorkspace.Create(objWorkspaceList.item[w].xml,(Self as TAtomService).Base,recurse));

          end;

        end;

      end;
    end;
  end;

end;

function TAtomService.asXMLObj(recurse:boolean=true):IXMLDOMDocument2;
var
  serviceDoc:IXMLDOMDocument2;
  XMLError:TXMLErrorInfo;
  objNodeChild, objAttributeNode: IXMLDOMNode;
  i:integer;
  objNodeList:IXMLDOMNodelist;
  c:integer;

  objServiceNode: IXMLDOMNode;
begin
  result := nil;
  serviceDoc := CreateOleObject('Msxml2.DOMDocument') as IXMLDOMDocument2;
  serviceDoc.async :=  False;
  serviceDoc.validateOnParse := false;
  serviceDoc.resolveexternals:=false;
  //serviceDoc.preserveWhiteSpace := true;

  //serviceDoc.setProperty('SelectionNamespaces', 'xmlns:atom='''+ Atom_NS + '''');
  serviceDoc.setProperty('SelectionNamespaces', 'xmlns:app='''+ Atom_APP_NS + '''');

  if FXML <> '' then begin
    //load from original source first. This way keep all foregin markup.
    serviceDoc.loadXML(FXML);
  end else begin
    objServiceNode := serviceDoc.createNode(NODE_ELEMENT,'service',Atom_APP_NS);
    serviceDoc.appendChild(objServiceNode);
  end;

  if (serviceDoc.readyState = 4) Then begin
    if (serviceDoc.parseError.errorCode <> 0) Then begin
      XMLError:=TXMLErrorInfo.create;
      XMLError.Title := MsgStrXMLInvalid;
      XMLError.Description := '';
      XMLError.Uri:='';
      XMLError.ErrorCode:=serviceDoc.parseError.errorCode;
      XMLError.Reason:=serviceDoc.parseError.reason;
      XMLError.SrcText:=serviceDoc.parseError.srcText;
      XMLError.Line:=serviceDoc.parseError.line;
      XMLError.LinePos:=serviceDoc.parseError.linepos;
      raise EXMLParseError.Create(XMLError);
      XMLError.Free;
      exit;
    end else begin
      objServiceNode := serviceDoc.documentElement;
      if objServiceNode.baseName <> 'service' then exit;
      if objServiceNode.namespaceURI <> Atom_APP_NS then exit;

      //@xml:base          use Base property here.
      if ((self as TAtomService).Base <> '') then begin
        objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xml:base');
        if objAttributeNode = nil then begin
          objAttributeNode := serviceDoc.createNode(NODE_ATTRIBUTE,'xml:base',XML_NS);
          objServiceNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomService).Base;
      end else begin
        objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xml:base');
        if Assigned(objAttributeNode) then objServiceNode.attributes.removeNamedItem('xml:base');
      end;
      //@xml:lang
      if ((self as TAtomService).Lang <> '') then begin
        objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xml:lang');
        if objAttributeNode = nil then begin
          objAttributeNode := serviceDoc.createNode(NODE_ATTRIBUTE,'xml:lang',XML_NS);
          objServiceNode.attributes.setNamedItem(objAttributeNode);
        end;
        objAttributeNode.text := (self as TAtomService).Lang;
      end else begin
        objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xml:lang');
        if Assigned(objAttributeNode) then objServiceNode.attributes.removeNamedItem('xml:lang');
      end;

      //@xmlns
      objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xmlns');
      if objAttributeNode = nil then begin
        objAttributeNode := serviceDoc.createNode(NODE_ATTRIBUTE,'xmlns',XML_NS);
        objAttributeNode.text := Atom_APP_NS;
        objServiceNode.attributes.setNamedItem(objAttributeNode);
      end;

      objAttributeNode := serviceDoc.documentElement.attributes.getNamedItem('xmlns:atom');
      if objAttributeNode = nil then begin
        objAttributeNode := serviceDoc.createNode(NODE_ATTRIBUTE,'xmlns:atom',XML_NS);
        objAttributeNode.text := Atom_NS;
        objServiceNode.attributes.setNamedItem(objAttributeNode);
      end;

      //delete workspaces
      objNodeList:=ServiceDoc.selectNodes('//app:service/app:workspace');
      if objNodeList <> nil then begin
        if objNodeList.length > 0 then begin
          for c:=0 to objNodeList.length-1 do begin
            objNodeList.item[c].parentNode.removeChild(objNodeList.item[c]);
          end;
        end;
      end;

      if recurse then begin
        if ((self as TAtomService).Workspaces.Count > 0) then begin
          for i:=0 to (self as TAtomService).Workspaces.Count-1 do begin
            if Assigned((self as TAtomService).Workspaces[i]) then begin
              objNodeChild := (self as TAtomService).Workspaces[i].asXMLObj.documentElement;
              if Assigned(objNodeChild) then begin
                objServiceNode.appendChild(objNodeChild);
              end;
            end;
          end;
        end;

      end;
      result:=serviceDoc;
    end;
  end;

end;


////////////////////////////////////////////////////////////////////////////////




end.
